Auto remote detection skips TERM/COLORTERM color depth parsing — monochrome truecolor-only output over SSH
Summary
In an auto-detected remote session (SSH_CONNECTION/SSH_CLIENT/SSH_TTY/MOSH_CONNECTION present in the forwarded host env), checkEnvironmentOverrides returns early and skips TERM/COLORTERM parsing. Both color caps (caps.rgb, caps.ansi256) stay false, so emitColor falls back to unconditional truecolor SGR (38;2/48;2). Terminals and multiplexers without truecolor support render everything in the default foreground — i.e. a monochrome UI over SSH, while the exact same TERM locally emits 38;5 and renders color correctly.
Observed with OpenTUI 0.4.5 and reproduced on current main (v0.5.11).
Environment
- Terminal chain: macOS Terminal.app → ssh → GNU screen 4.09 (
TERM=screen-256color,COLORTERMunset) - Any OpenTUI CLI app using
createCliRendererwith default options (e.g. OpenCode):remoteModeunset →.auto; host env is forwarded viaDEFAULT_FORWARDED_ENV_KEYS(which includes both the SSH markers andTERM/COLORTERM), soenv_is_forwardedis true - Client terminal supports 256 colors; the multiplexer passes
38;5through but drops/garbles38;2
Reproduction
sshinto a host, startscreen(termscreen-256color)- Run any OpenTUI-based TUI that renders RGB colors
- All themed output is monochrome; a raw capture shows
38;2;r;g;bsequences only - The same app, same
TERM, without the SSH marker vars (env -u SSH_CONNECTION -u SSH_CLIENT -u SSH_TTY -u MOSH_CONNECTION ...) emits38;5;nand renders color
Binary-search evidence (probe on the real renderer, TERM=screen-256color, COLORTERM empty):
| Conditions | SGR output |
|---|---|
| no SSH vars | 38;5 only |
| SSH vars present | 38;2 only |
SSH vars + COLORTERM=truecolor |
38;2 only (correct — truecolor requested) |
Root cause
packages/native/src/terminal.zig (checkEnvironmentOverrides):
const env_is_forwarded = if (self.host_env_map) |*host_env_map| env_map == host_env_map else false;
self.applyKnownUnicodeWidthIdentity();
if (self.opts.remote_mode == .auto and self.remote and env_is_forwarded) {
return; // skips TERM "256color" -> caps.ansi256 and COLORTERM -> caps.rgb
}The early return is there to avoid trusting forwarded terminal identity heuristics (TERM_PROGRAM etc.) in remote sessions — the host env describes the server shell, not the client terminal. But TERM and COLORTERM are different: sshd adopts TERM from the client's PTY request, and the docs for remote mode recommend forwarding exactly ["TERM", "COLORTERM"] as endpoint-describing keys. Skipping them leaves no capability source at all: terminal query responses only ever set rgb (kitty detection), never ansi256, so a 256-color-capable remote terminal is indistinguishable from a dumb one.
Workaround
Strip the SSH marker vars when launching the app:
env -u SSH_CONNECTION -u SSH_CLIENT -u SSH_TTY -u MOSH_CONNECTION <app>Fix
Apply the color-depth keys (TERM 256color → ansi256, COLORTERM → rgb) before the early return, keeping the return for the identity heuristics. PR incoming.
Note: this issue was diagnosed and resolved fully by an AI agent (OpenCode, automated mode) — reproduction, root-cause analysis, fix, and tests were produced automatically from the investigation session.
Source: anomalyco/opentui