TMUX Bug: Composer/user-message background pill missing inside tmux (OSC 11 query blocked)
What version of Codex CLI is running?
.125
What subscription do you have?
pro
Which model were you using?
5.4
What platform is your computer?
MacOS
What terminal emulator and version are you using (if applicable)?
VS Code, TMUX
What issue are you seeing?
Summary
When Codex runs inside a tmux session, the input composer and user-message backgrounds render with no fill. Outside tmux they render with the expected adaptive grey/light pill. Reproducible on macOS with Codex 0.125.0 and tmux 3.6a.
Root cause
codex-rs/tui/src/style.rs computes the pill background by blending against the terminal's actual bg:
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(), // ← no pill
}
}
terminal_bg comes from default_bg() in terminal_palette.rs, which sends a plain OSC 11 query via crossterm::style::query_background_color. Tmux does not forward plain OSC 11 to the outer terminal and does not reliably reply itself, so the query times out and default_bg() returns None. The None arm has no fallback, so the pill disappears entirely.
Codex already wraps OSC 52 (clipboard) in tmux DCS passthrough at clipboard_copy.rs:324. The same treatment isn't applied to OSC 11.
What steps can reproduce the bug?
Outside tmux — pill renders
codex
Inside tmux — no pill, even with allow-passthrough on
tmux new-session 'codex'
In both cases the host terminal (VS Code integrated terminal, iTerm2, etc.) responds to OSC 11; tmux is the only differentiator.
What is the expected behavior?
Suggested fixes (any one would resolve)
When terminal_info().multiplexer == Some(Multiplexer::Tmux { .. }), wrap the OSC 11 query in tmux DCS passthrough (\ePtmux;\e\e]11;?\e\\\e\\) and parse the unwrapped reply — same pattern already used for OSC 52.
Add a config-level explicit fallback, e.g. [tui] background_rgb = [r, g, b], so users on multiplexers can pin a sensible value.
Add a sane default constant for the None arm of user_message_style_for (e.g. (30, 30, 30) for dark / (245, 245, 245) for light, picked by some other heuristic).
Workaround
None known at the user level. tmux set -g allow-passthrough on enables DCS-wrapped escapes through tmux, but Codex sends OSC 11 plain, so the option doesn't take effect.
Comparison
Gemini CLI hardcodes its TUI colors (no OSC 11 query) and renders correctly inside tmux on the same host. Claude Code TUI does the same. Codex is the only one of the three that depends on the runtime query.
Environment
Codex CLI 0.125.0 (npm @openai/codex)
macOS Darwin 25.4.0 (arm64)
tmux 3.6a
VS Code integrated terminal (also reproduces in iTerm2 + Terminal.app)
Additional information
_No response_
3 Comments
I attempted to replicate the issue using the same tools: VSCode + tmux 3.6a + codex 0.125.0 but I couldn't:
<img width="1980" height="1680" alt="Image" src="https://github.com/user-attachments/assets/c0896289-084e-4cfd-90cc-bb473d2b223e" />
Would you mind sharing your macOS and VSCode versions and your
.tmux.conf?Thanks for the reply! You confirmed it was a self-inflicted wound :)
Re-opening signal: the bounded-probes fix in #20654 does not restore the missing colors, only makes the failure faster. The issue described here (composer/user-message background pill missing inside tmux, plus broader monochrome rendering) is still reproducible on
main.Reproduction
0.129.0-alpha.2(npm@openai/codex)3.5a1.3.2allow-passthrough on,terminal-features ',xterm-ghostty:RGB',update-environment "COLORTERM TERM_PROGRAM"Byte-level evidence
Captured codex's raw stdout via
scriptto make this objective:| Environment | 24-bit SGR | 256-color SGR | 16-color SGR |
|---|---|---|---|
| Outside tmux | 0 | 71 | 860 |
| Inside tmux (attached client,
xterm-ghostty) | 0 | 0 | 0 |Inside tmux, codex emits only
[0m,[1m,[2m,[3m,[22mand bare[m/[;mresets — never any color SGR. This holds across all attempted env permutations:env -u TMUX,env -u TMUX_PANE,env TERM=xterm-ghostty,env TERM_PROGRAM=ghostty,env CLICOLOR_FORCE=1,env TERM=xterm-direct, andFORCE_COLOR=3— all monochrome.Detection path is correct
Confirmed inside the affected tmux pane:
TERM_PROGRAM=tmux,TERM_PROGRAM_VERSION=3.5a,TMUX_PANE=%87tmux display-message -p '#{client_termname}'→xterm-ghosttytmux display-message -p '#{client_termtype}'→ghostty 1.3.2-HEAD+...tmux display-message -p '#{client_termfeatures}'→bpaste,ccolour,clipboard,cstyle,focus,RGB,titleSo
terminal-detectioncorrectly reachesTerminalName::Ghosttyvia the tmux-query path. The break is downstream:terminal_palette::default_bg()returnsNonebecause the plain OSC 11 query times out, andstyle::user_message_style_for(None)returnsStyle::default()with no fill — exactly the path called out in the original issue. That code is unchanged onmain.Why bounded probing alone isn't enough
#20654 reduces the probe timeout from ~2s to ~100ms, which is good UX (no startup stall). But it doesn't change the answer: with no DCS-wrapping,
query_background_colorstill returns nothing inside tmux,default_bg()still returnsNone, and the no-fallbackNonearm inuser_message_style_forstill renders monochrome.Suggested fix (unchanged from original issue)
When
terminal_info().multiplexer == Some(Multiplexer::Tmux { .. }), wrap the OSC 11 probe in tmux DCS passthrough:This is the same pattern already used at
clipboard_copy.rs:324for OSC 52. Users who already haveset -g allow-passthrough onin their tmux config (the documented requirement) would then get correct backgrounds without further configuration. A small constant fallback in theNonearm ofuser_message_style_for(e.g.(30,30,30)for dark detection) would also catch terminals that don't support OSC 11 at all.Happy to test a patch.