statusLine renders 24-bit ANSI colors quantized to 256-color palette when running inside tmux, even though the terminal and tmux both support truecolor
Environment
- Claude Code version: 2.1.220
- OS: Arch Linux, kernel 7.0.12-arch1-1
- Terminal: kitty 0.48.0
- Multiplexer: tmux 3.7b
TERMinside tmux pane:tmux-256colorCOLORTERMinside tmux pane:truecolor(confirmed present, not stripped)- tmux config:
default-terminal "tmux-256color",terminal-overrides ',*:RGB' tmux info/client_termfeaturesfor the attached kitty client explicitly listRGBas a negotiated feature
Summary
A custom statusLine command that emits raw 24-bit ANSI truecolor escape codes (e.g. \033[38;2;214;93;14m) renders with pixel-perfect accurate colors when Claude Code is run directly in kitty, but renders visibly washed-out/desaturated colors when the identical session is run inside a tmux pane. The colors are not merely "duller" — they land exactly on the standard xterm 256-color 6×6×6 cube grid (channel values restricted to 0/95/135/175/215/255), which is the unmistakable signature of 24-bit RGB being quantized down to an indexed 256-color palette.
Reproduction
- Configure a
statusLinecommand (~/.claude/settings.json) that outputs raw\033[38;2;r;g;bm/\033[48;2;r;g;bmtruecolor escape sequences. - Run
claudedirectly in kitty. Screenshot the statusline. - Run
claudeinside a tmux pane (same kitty window, same tmux config as above). Screenshot the statusline. - Compare pixel colors between the two screenshots.
Evidence
Sampled pixel RGB values from screenshots of the same statusline segments:
| Segment | Configured truecolor value | Rendered outside tmux | Rendered inside tmux (via Claude Code) |
|---|---|---|---|
| orange | (214, 93, 14) | (214, 93, 14) ✅ | (215, 135, 0) ❌ |
| yellow | (215, 153, 33) | (215, 153, 33) ✅ | (215, 175, 95) ❌ |
| aqua/teal | (104, 157, 106) / (69, 133, 136) | exact match ✅ | (135, 175, 135) / (95, 175, 175) ❌ |
| gray | (102, 92, 84) / (60, 56, 54) | exact match ✅ | (135, 135, 135) / (95, 95, 95) ❌ |
Every "inside tmux" value falls exactly on the xterm 256-color cube's fixed channel levels (0, 95, 135, 175, 215, 255) — not just close to it, but exact.
Isolating the cause: it's not tmux or kitty
To rule out tmux/kitty as the culprit, the same raw ANSI codes were tested by bypassing Claude Code entirely, typed directly into the same live tmux pane:
printf '\033[48;2;214;93;14m \033[0m'run directly in the tmux shell → rendered pixel-perfect(214, 93, 14).- The exact statusLine script's raw stdout (
bash statusline.sh) run directly in the tmux shell (not via Claude Code) → rendered pixel-perfect truecolor for every segment.
This proves tmux and kitty both correctly pass through and render 24-bit color end-to-end, including inside this exact pane, with this exact config. The quantization only occurs when Claude Code itself renders the statusLine command's output.
Expected behavior
Claude Code should either:
- Pass the statusLine command's raw stdout through unmodified (it's already valid ANSI), or
- If it does its own capability detection, correctly honor
COLORTERM=truecolorrather than assuming 256-color support fromTERM=tmux-256coloralone.
Actual behavior
Claude Code appears to detect terminal color capability (likely via TERM) and downsamples/quantizes the statusLine's ANSI truecolor codes to a 256-color-safe palette when it believes it's inside a "256color" terminal — even when COLORTERM=truecolor is correctly set and the terminal/multiplexer chain has been independently verified to support and correctly render true 24-bit color.
Additional notes
- The
statusline.mddocs only say "use ANSI escape codes... terminal must support them" with no detail on capability detection or COLORTERM handling. - Happy to provide the full statusline script and settings.json, plus the original screenshots and pixel-sampling scripts used to produce the table above, on request.