TMUX Bug: Composer/user-message background pill missing inside tmux (OSC 11 query blocked)

Resolved 💬 3 comments Opened Apr 27, 2026 by gprigge Closed Apr 28, 2026
💡 Likely answer: A maintainer (fcoury-oai, contributor) responded on this thread — see the highlighted reply below.

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_

View original on GitHub ↗

3 Comments

fcoury-oai contributor · 4 months ago

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?

gprigge · 4 months ago

Thanks for the reply! You confirmed it was a self-inflicted wound :)

Minoo7 · 3 months ago

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

  • Codex CLI: 0.129.0-alpha.2 (npm @openai/codex)
  • tmux: 3.5a
  • Outer terminal: Ghostty 1.3.2
  • tmux config: allow-passthrough on, terminal-features ',xterm-ghostty:RGB', update-environment "COLORTERM TERM_PROGRAM"

Byte-level evidence

Captured codex's raw stdout via script to 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, [22m and bare [m/[;m resets — 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, and FORCE_COLOR=3 — all monochrome.

Detection path is correct

Confirmed inside the affected tmux pane:

  • TERM_PROGRAM=tmux, TERM_PROGRAM_VERSION=3.5a, TMUX_PANE=%87
  • tmux display-message -p '#{client_termname}' → xterm-ghostty
  • tmux display-message -p '#{client_termtype}' → ghostty 1.3.2-HEAD+...
  • tmux display-message -p '#{client_termfeatures}' → bpaste,ccolour,clipboard,cstyle,focus,RGB,title

So terminal-detection correctly reaches TerminalName::Ghostty via the tmux-query path. The break is downstream: terminal_palette::default_bg() returns None because the plain OSC 11 query times out, and style::user_message_style_for(None) returns Style::default() with no fill — exactly the path called out in the original issue. That code is unchanged on main.

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_color still returns nothing inside tmux, default_bg() still returns None, and the no-fallback None arm in user_message_style_for still renders monochrome.

Suggested fix (unchanged from original issue)

When terminal_info().multiplexer == Some(Multiplexer::Tmux { .. }), wrap the OSC 11 probe in tmux DCS passthrough:

\ePtmux;\e\e]11;?\e\\\e\\

This is the same pattern already used at clipboard_copy.rs:324 for OSC 52. Users who already have set -g allow-passthrough on in their tmux config (the documented requirement) would then get correct backgrounds without further configuration. A small constant fallback in the None arm of user_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.