Do not rely on startup OSC probing for the composer background

Open 💬 5 comments Opened May 19, 2026 by haowang02
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

codex-cli 0.131.0

What subscription do you have?

ChatGPT Pro 20x

Which model were you using?

gpt-5.5

What platform is your computer?

Linux 6.8.0-90-generic x86_64 x86_64

What terminal emulator and version are you using (if applicable)?

iTerm2 (ssh)

Codex doctor report

What issue are you seeing?

Codex uses startup OSC 10/11 probing to detect terminal colors for the TUI composer background. This is unreliable over SSH because the response has to round-trip through the local terminal emulator.

Relevant code:

  • Startup probe timeout is 100ms: codex-rs/tui/src/terminal_probe.rs

``rust
pub(crate) const DEFAULT_TIMEOUT: Duration = Duration::from_millis(100);
``

  • Default colors require both OSC 10 and OSC 11:

``rust
fn parse_default_colors(buffer: &[u8]) -> Option<DefaultColors> {
let fg = parse_osc_color(buffer, /*slot*/ 10)?;
let bg = parse_osc_color(buffer, /*slot*/ 11)?;
Some(DefaultColors { fg, bg })
}
``

  • Composer background depends on user_message_style():

``rust
pub fn user_message_style_for(terminal_bg: Option<(u8, u8, u8)>) -> Style {
match terminal_bg {
Some(bg) => Style::default().bg(user_message_bg(bg)),
None => Style::default(),
}
}
``

In my SSH session, OSC 10/11 are supported, but the responses always arrive after 100ms. As a result, the composer background appears locally, but disappears when running Codex over SSH.

<img width="596" height="250" alt="Image" src="https://github.com/user-attachments/assets/5e93503d-90f6-4ec3-95c7-fb1f3339007f" />

Please consider removing this startup probe as a hard dependency for the composer background. Possible alternatives:

  • use a theme/default composer background without probing;
  • probe asynchronously and update later;
  • retry instead of caching failure;
  • allow users to configure the composer background.

Increasing the timeout would help some cases, but the core issue is that fixed startup OSC probing is fragile over SSH.

What steps can reproduce the bug?

  1. Run Codex on the remote machine.
  2. Observe that the composer/input background is missing.

The bug is reproducible when OSC 10/11 are supported but the response arrives after Codex's 100ms startup probe timeout.

What is the expected behavior?

_No response_

Additional information

_No response_

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #22761

Powered by Codex Action

ssirois · 2 months ago

I think this issue also has a more severe startup-input failure mode than missing composer background colors.

I can reproduce a case where Codex exits before the user can interact with startup dialogs, including the project trust selector and session selector, when started inside GNU screen running under rxvt-unicode (urxvt). The same Codex build works when started directly in urxvt, and it also works in xterm both with and without GNU screen.

Reproduced with:

  • OS: Debian GNU/Linux 11 (bullseye)
  • kernel: Linux 5.10.0-44-amd64 x86_64
  • Codex CLI: codex-cli 0.141.0
  • Codex path: $HOME/.nvm/versions/node/v20.20.0/bin/codex
  • terminal emulator: rxvt-unicode (urxvt) v9.22, released 2016-01-23
  • multiplexer: GNU screen 4.08.00, released 2020-02-05
  • startup dialog affected: project trust selector, and the same behavior is expected for other startup dialogs
  • a local build from a focused patch fixes the observed behavior in this environment

The same Codex CLI version is interactive when run directly in the same urxvt window without GNU screen. In the same local environment, xterm also works both with and without GNU screen (xterm reported XTerm(366) in the captured environment).

Relevant environment from the working direct-urxvt run:

TERM=rxvt-unicode-256color
COLORTERM=rxvt-xpm
STY=
WINDOW=
WINDOWID=23068681
CODEX_TUI_DISABLE_KEYBOARD_ENHANCEMENT=

Relevant environment from the failing urxvt + screen run in the same window:

TERM=screen-256color-bce
COLORTERM=rxvt-xpm
STY=114447.pts-1.<redacted hostname>
WINDOW=0
WINDOWID=23068681
CODEX_TUI_DISABLE_KEYBOARD_ENHANCEMENT=1

CODEX_TUI_DISABLE_KEYBOARD_ENHANCEMENT=1 was an early negative-control test in this specific environment. The goal was to check whether Codex's keyboard enhancement path was responsible for the observed input problem. The failure still reproduced with keyboard enhancement disabled, and the later strace comparison pointed instead to delayed terminal response bytes from the startup OSC 10/11 default-color probe. I included the variable above only because it documents that the keyboard-enhancement path was evaluated and did not explain this failure.

The failure mechanism appears to be in the startup OSC 10/11 default-color probe, not in the dialog code itself.

From strace comparison between the failing urxvt + screen run and a working direct-urxvt run:

  • Codex writes a startup probe containing cursor-position query plus OSC 10/11 default-color queries.
  • The OSC 10/11 queries are terminated with ST (ESC \).
  • In the failing urxvt + screen path, Codex reads the cursor response and an incomplete OSC 11 body before the bounded startup-probe timeout expires.
  • After the trust selector is already drawn and the normal TUI input path is active, the remaining ST terminator contributes a standalone ESC byte to input.
  • The dialog correctly treats Escape as cancel/quit, so Codex exits immediately before the user can select anything.

This is related to this issue because it is another consequence of relying on startup OSC probing with a short deadline. The symptom is different from the composer background disappearing: the delayed terminal response leaks into the interactive input stream and triggers an actual dialog action.

I tested a minimal local fix that keeps the OSC 10/11 default-color probe but terminates those queries with BEL (\x07) instead of ST (ESC \). The parser already accepts both BEL and ST responses. With BEL termination, the same urxvt + screen startup trust selector becomes interactive again in my environment.

This also matches existing precedent in the repository: PR #18261 changed terminal title OSC updates from ST to BEL because an ST terminator could leak through terminal integrations. This startup color-probe case appears to be the same class of robustness issue, but on the input side of startup probing.

Related context:

  • #20654 introduced bounded startup terminal probes, including OSC 10/11 default-color probing.
  • #23175 optimized/batched startup terminal probes.
  • #28342 handled terminal response byte injection after suspend/resume.
  • #18261 changed OSC title updates to BEL termination to avoid leaked ST behavior.
  • Older OSC reply leakage reports such as #4945, #5107, #4960, #5852, and #6081 look related historically, but they were about visible 10;rgb / 11;rgb text entering input and were closed as fixed by older work.

The focused patch I tested locally changes only the OSC 10/11 query terminator from ST to BEL in the startup default-color probe. It does not disable default-color probing, does not special-case urxvt, GNU screen, or $TERM, and does not change dialog handling.

ssirois · 2 months ago

As per contributing.md, I did not open a PR but you can inspect the patch that I have tested locally within the above described environment

mehulparmariitr · 1 month ago

Implementation update for the shared OSC 10/11 subsystem:

The focus fix replaces sequential multi-second Crossterm waits with the existing batched probe under one shared 100 ms deadline, pauses/recreates the event source around that probe, and preserves previously cached colors when refresh times out.

It does not change the startup deadline or solve a missing initial color cache over high-latency SSH. This issue therefore remains the relevant appearance/remote tradeoff when reviewing the bounded focus change.

adaex · 12 days ago

**Additional data point: intermittent total probe failure over VS Code Remote SSH (not just slow OSC 10/11)**

I'm hitting this on VS Code Remote SSH (Linux, codex-cli 0.147.0) and want to add evidence for a failure mode that's a bit different from the "OSC responses always arrive after 100ms" case in the original report.

Symptom: the composer/UI background is intermittently dark — sometimes light, sometimes dark, across otherwise-identical launches in the same terminal.

Instrumented both sides. I enabled log_dir to capture codex's own terminal startup probes completed line, and separately confirmed (via an external TIOCSTI pre-fill of OSC 10/11 replies before launch) whether a reply was sitting in the tty input queue. Aligned by timestamp:

# codex side (codex-tui.log)
05:03:12  duration_ms=99  cursor_position=false default_colors=false keyboard_enhancement_supported=None   <- DARK
05:03:15  duration_ms=55  cursor_position=true  default_colors=true  keyboard_enhancement_supported=Some(true)
05:03:21  duration_ms=59  cursor_position=true  default_colors=true  ...
05:03:24  duration_ms=76  cursor_position=true  default_colors=true  ...
05:03:27  duration_ms=86  cursor_position=true  default_colors=true  ...
05:03:38  duration_ms=99  cursor_position=false default_colors=false keyboard_enhancement_supported=None   <- DARK

Two observations that I think are new relative to the current thread:

  1. **When it fails, all three probes fail together** — cursor_position, default_colors, and keyboard_enhancement are simultaneously false and the read window burns the full ~99ms. Cursor position (ESC[6n) is answered by the local terminal and doesn't need an OSC round-trip, so a failure that also loses the cursor response points to the whole read window getting nothing, rather than only the OSC 10/11 reply being slow.
  1. Pre-injecting a complete OSC 10/11 reply into the tty does not reliably rescue it. At 05:03:12 a full 48-byte OSC 10/11 reply was confirmed present in the tty queue, yet codex still reported default_colors=false. This suggests the fd codex actually reads (it dups STDIN) can become decoupled from the terminal that answers queries — note stdout is not a TTY in this environment (VS Code shell integration wraps it), so STDIN and /dev/tty are not guaranteed to be the same endpoint.

Rough failure rates (small sample, my machine, simulating the same 100ms-bounded probe):

  • plain VS Code integrated terminal over SSH: ~3/36 launches fail
  • inside tmux (tmux-256color): 6/6 fail, and they still fail with a 600ms timeout — consistent with tmux not forwarding OSC 10/11 by default.

Why this matters for the fix direction: since the failure is intermittent and can survive a correct pre-injected reply, workarounds that race the probe (injecting replies, bumping the timeout) reduce the frequency but can't make it deterministic. In line with this issue's title, it would help to let users declare the background explicitly and skip the probe entirely — e.g. an env/config like CODEX_TUI_ASSUME_BG=light|dark|<hex> (or a tui.assume_background setting) that seeds default_bg() and bypasses startup color detection. That gives remote/multiplexer users a deterministic escape hatch without changing probe behavior for everyone else.

Happy to share the raw logs or the probe-simulation script if useful.