CLI freezes on FocusGained in Zellij while requerying terminal palette
What version of Codex CLI is running?
codex-cli 0.144.3 (also reproduced on 0.144.1)
codex doctor --json reports 0.144.4 is now available. The same synchronous focus-query path is still present on main at commit b24aa20107f365a1d0f06de9e0b28df5c516c7dd.
What subscription do you have?
ChatGPT subscription. The failure occurs in terminal focus handling before any model-dependent work.
Which model were you using?
gpt-5.6-sol (not relevant to the failure)
What platform is your computer?
Darwin 25.5.0 arm64 arm
macOS 26.5.1
What terminal emulator and version are you using?
- Ghostty 1.3.1
- Zellij 0.44.3
Codex doctor report
Relevant redacted fields from codex doctor --json:
{
"overallStatus": "warning",
"codexVersion": "0.144.3",
"auth": {
"mode": "chatgpt",
"status": "ok"
},
"config": {
"model": "gpt-5.6-sol",
"modelProvider": "openai",
"status": "ok"
},
"installation": {
"installMethod": "npm",
"platform": "macos-aarch64",
"status": "ok"
},
"system": {
"os": "Mac OS 26.5.1 [64-bit]",
"locale": "C.UTF-8"
},
"terminal": {
"TERM_PROGRAM": "ghostty",
"terminal": "Ghostty",
"terminalVersion": "1.3.1",
"multiplexer": "zellij 0.44.3",
"COLORTERM": "truecolor"
}
}
The only doctor warning concerns old rollout files and is unrelated to terminal input.
What issue are you seeing?
After a FocusGained event, the Codex TUI can freeze indefinitely inside Zellij. The process remains alive, but the UI stops redrawing and does not accept keyboard input.
A macOS process sample taken while frozen showed the main thread blocked under:
TuiEventStream::map_crossterm_event
-> terminal_palette::requery_default_colors
-> crossterm foreground/background color query
-> terminal response read/poll
On the current source path, Event::FocusGained synchronously calls requery_default_colors() while crossterm's event stream is active. The Unix implementation performs foreground and background OSC queries without a timeout. When the expected terminal response is absent or not delivered to that reader through Zellij, focus handling never returns.
As a diagnostic, I patched the installed ARM64 requery_default_colors function to return immediately, ad-hoc re-signed the binary, and launched it in a fresh PTY. It survived three focus-loss/focus-gain cycles and continued accepting input, including /quit. This isolates the freeze to the palette requery rather than general focus handling.
What steps can reproduce the bug?
- Open Ghostty 1.3.1 on macOS 26.5.1.
- Start Zellij 0.44.3.
- Start Codex CLI 0.144.3 inside a project.
- Move focus away from Ghostty and then focus it again.
- Repeat the focus cycle if needed.
- Observe that Codex eventually stops redrawing and accepting input while the process remains alive.
- Sample the process with
sample <pid>; the main thread is blocked in the terminal palette requery path described above.
What is the expected behavior?
Focus handling should always return promptly. Codex may refresh terminal theme colors after focus is regained, but a missing OSC response must not freeze the TUI.
Additional information
There is already a bounded terminal color probe used during startup: terminal_probe::default_colors(DEFAULT_TIMEOUT), where DEFAULT_TIMEOUT is currently 100 ms.
A narrow fix is to:
- pause the Unix
EventBrokerwhen handlingFocusGained; - refresh the palette using the existing bounded direct-TTY probe;
- resume the event source before returning
TuiEvent::Draw.
Pausing the event broker prevents the bounded direct-TTY probe from racing crossterm's background input reader, while the timeout guarantees that focus handling cannot block indefinitely. This preserves focus-triggered theme refresh rather than removing it.
I implemented this two-file change locally along with a Unix regression test that sends FocusGained, verifies the redraw and broker restart, and then verifies that a key event is delivered by the restarted source. Validation completed successfully:
just test -p codex-tui
Summary: 3046 tests run: 3046 passed, 4 skipped
just fix -p codex-tui
just fmt
I can provide the local patch if the team would like it or invites a pull request.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗