Enter key stops responding after task completion in tmux (TUI input stuck)
Bug Description
After a model turn completes (or after interrupting with Escape), the composer input field accepts text characters but Enter (submit) no longer responds. This happens consistently when running codex inside tmux.
Environment
- codex-cli: 0.104.0 (Homebrew, aarch64-apple-darwin)
- macOS 15.4 (arm64)
- tmux 3.5a (inside byobu)
- Terminal: iTerm2
Steps to Reproduce
- Launch
codexinside a tmux session - Send a prompt and let the model complete a turn
- Type a new prompt in the composer
- Press Enter → nothing happens
- Text remains in the prompt,
turns:0shown in statusbar
Expected
Enter should submit the text and trigger a new turn.
Actual
- Characters are received and displayed in the composer
- Enter key is completely ignored
tmux send-keys C-malso fails- Only workaround: Ctrl+C to kill the session and restart
Analysis
Looking at the source, codex enables the kitty keyboard enhancement protocol:
// tui.rs:73-79
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
)
tmux has limited support for the kitty keyboard protocol. After a model turn completes, the terminal state may not properly reset the keyboard enhancement flags, causing the Enter key event (KeyCode::Enter with KeyModifiers::NONE) to not match the expected pattern in chat_composer.rs:1396.
The EventBroker pause/resume cycle during external command execution (with_restored) may also leave the crossterm EventStream in a state where key events are silently dropped.
Suggested Fix
- Add a fallback in
handle_key_eventthat accepts raw\r(0x0d) as Enter even when keyboard enhancement reports don't match - Or re-push keyboard enhancement flags after each turn completes
- Or detect stale input state and auto-recover
Workaround
Kill codex with Ctrl+C and restart the session.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Stumbled upon a similar issue. This workaround helped:
I investigated this on current
mainand found a concrete TUI-side failure mode that is consistent with the reported tmux symptom:TuiEventStream::poll_crossterm_event()resets the broker toStarton a crosstermErr/EOF, but the current code returnsNoneto the outer app loop. The outer loop only listens onSome(event) = tui_events.next(), so one input-stream failure can leave the TUI process alive while keyboard input never recovers until process restart.I prepared a fix branch that recreates the shared event source after a short backoff instead of terminating input handling, and adds regression coverage for both the
ErrandEOFrecovery paths:https://github.com/openai/codex/compare/main...strato-space:tui-input-recovery
While tracing adjacent code I also found a separate modal-lifecycle inconsistency in
BottomPane::handle_paste(). That is tracked separately in #16192 so this issue can stay focused on the input-stream recovery bug.I still could not open the PR from this environment because GitHub rejected
CreatePullRequestfor the current token, but the branch is public and ready for review.Confirmed a related tmux/TUI stale-state symptom on current
v0.118.0on Linux.Environment:
v0.118.0tmuxomxwrapper, but the live TUI process is the normal/usr/bin/codexsessionWhat happened:
019d61b9-5b70-78e2-9a11-56ebcfd12bbarecordedtask_completeat2026-04-06T10:55:05.491ZImportant operator detail: the last interactive action before the stale state was switching back from the subagent to the main agent via
/agent. I did not find a literal/agenttoken in the saved rollout, so that last step is from operator recollection rather than a logged command event, but the timing lines up with the subagent completion boundary.So this does not look like a backend wait deadlock. The backend completed normally. It looks like another TUI/input/thread-switch state failure where the process stays alive but the interactive state does not recover cleanly after completion.
This feels adjacent to the issue here, but with a subagent/main-agent transition as the trigger.
this works perfectly