Skip to content

Interactive TUI renders but never subscribes to stdin — all keyboard input is ignored #28799

Description

@ArcheeNic

What happened?

The interactive TUI renders completely — banner, tips, the folder-trust dialog, the auth-selection dialog, and eventually the input prompt — but no keyboard input is ever processed. Digits, arrows, Enter, plain characters: nothing changes the screen, ever. The process stays alive and idle.

This is not a rendering hang and not a TTY problem. The process never subscribes to terminal input at all.

Evidence

With the TUI up and waiting, on the main CLI process:

  1. The tty fd is absent from every epoll set. Dumping /proc/<pid>/fdinfo/<epoll fd> for all three epoll instances shows no tfd entry for fd 0 or for the separate /dev/pts/N descriptor the CLI opens. The main thread sits in epoll_pwait(..., -1, ...) forever on a set that contains only pipes and eventfds.

  2. No bytes are ever read. rchar in /proc/<pid>/io does not increase by a single byte across repeated keypresses, while the terminal itself is in raw mode (stty -a on the pts reports -icanon -echo -isig, so the CLI did call setRawMode(true)).

  3. strace confirms the capability query completes, then nothing follows. Launching under strace -ff -yy -e trace=read,ioctl,epoll_ctl,epoll_wait:

ioctl(23</dev/pts/N>, TCSETSW, {c_iflag=IUTF8, ... c_lflag=ECHOE|ECHOK|ECHOCTL|ECHOKE, ...}) = 0
read(23</dev/pts/N>, "\33P>|tmux 3.4\33\\\33[?1;2;4c", 65536) = 23

The terminal replies to \e[>q (name) and \e[c (device attributes), but never to \e[?u (kitty keyboard) — neither tmux nor VTE support it. After this read there is not a single epoll_ctl(EPOLL_CTL_ADD) for the tty fd in any traced process, and the typed bytes never appear in any trace.

  1. It is a race, not a deterministic failure. In one run out of roughly twenty, the tty descriptor was present in an epoll set and input worked normally — the typed character appeared in the prompt immediately. Same binary, same terminal, same flags.

Suspected cause

TerminalCapabilityManager.detectCapabilities() attaches a data listener to process.stdin (flowing mode) and waits up to its 1000 ms timeout. Because the terminal never answers the kitty query, that full timeout is always consumed on terminals like VTE and tmux.

Meanwhile Ink subscribes via readable, and KeypressContext only calls setRawMode(true) when stdin.isRaw === false — while raw mode is already enabled by the time that effect runs. Ink's setRawMode is what attaches the readable listener (guarded by rawModeEnabledCount === 0), so the listener is never attached and the fd never enters the event loop.

Mixing flowing-mode (on('data')) capability detection with Ink's readable-based reader on the same stream looks like the underlying race.

Note: patching the wasRaw === false guard to call setRawMode(true) unconditionally, in every bundled copy of the chunk, did not fix it — so the guard alone is not the whole story.

Steps to reproduce

Reproduction is timing-dependent; on the affected machine it fails on essentially every launch.

  1. On Linux, run gemini in GNOME Terminal (VTE) or inside tmux.
  2. Wait for the folder-trust dialog, or use --skip-trust plus GEMINI_API_KEY to reach the input prompt directly.
  3. Press digits, arrows, Enter, or type any text.
  4. Nothing is registered. Confirm with grep rchar /proc/<pid>/io before and after — the value does not move.

What did you expect to happen?

Keyboard input to be processed by the TUI.

Environment

  • OS: Linux (kernel 7.0), Wayland
  • Terminals: GNOME Terminal (VTE) and tmux 3.4 — both affected
  • Node.js: 24.19.0 and 22.23.2 — both affected
  • Gemini CLI: 0.50.0, 0.53.1, 0.54.4, 0.55.1 and 0.56.0-nightly — all affected
  • Installed globally via npm (user-level prefix, no sudo)

Ruled out

  • Not a TTY issue: a plain Node script in the same terminal does process.stdin.setRawMode(true) and receives a, �[B, \r correctly.
  • Not configuration: reproduced with a completely empty HOME, no settings file, no extensions.
  • Not the relaunch wrapper: GEMINI_CLI_NO_RELAUNCH=1 behaves identically, with a single process holding the tty.
  • Not ui.renderProcess: setting it to false changes nothing.
  • Not the secret service, network reachability, io_uring (UV_USE_IO_URING=0 makes no difference), or terminal resize (SIGWINCH does not wake it).
  • Non-interactive mode (gemini -p "...") works perfectly.

Possibly related: #23297 (Enter does nothing), #23480 (a different mechanism — a background process stealing stdin — but the same user-visible symptom).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/coreIssues related to User Interface, OS Support, Core Functionalityeffort/large3+ days: platform-specific, architecture, memory leakskind/bugpriority/p1Important and should be addressed in the near term.status/manual-triage

    Type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions