Windows Terminal detection via WT_SESSION changes TUI background from white to black

Open 💬 6 comments Opened Aug 10, 2026 by LazyHeidi

Summary

On Windows, Codex CLI renders with a black background when launched inside Windows Terminal, but renders with a white background when WT_SESSION is absent.

The key point is that the terminal host itself does not need to change: removing only the WT_SESSION environment variable inside the same Windows Terminal session changes Codex from black to white.

Environment

  • Codex CLI: 0.147.0
  • Installation: npm
  • OS: Windows 11 Pro (10.0.26200)
  • PowerShell: 7.6.4
  • Windows Terminal / ConPTY when reproducing the black rendering

Reproduction

  1. Open PowerShell 7 in Windows Terminal.
  2. Run:
codex
  1. Codex renders with the undesired black background.
  2. Exit Codex.
  3. In the same Windows Terminal tab, run:
Remove-Item Env:WT_SESSION -ErrorAction SilentlyContinue
codex
  1. Codex now renders with the expected white background.

codex doctor --json difference

With the normal Windows Terminal environment:

WT_SESSION: present
terminal: Windows Terminal

After removing only WT_SESSION:

WT_SESSION: absent
terminal: unknown

Other terminal characteristics remain effectively the same, including stdin/stdout/stderr being terminals and VT processing being enabled.

Additional checks

I also compared the Win32 console color information returned by GetConsoleScreenBufferInfoEx() between:

  • Windows Terminal -> PowerShell 7
  • directly launched pwsh.exe -> conhost

Both returned the same values:

wAttributes : 0x0007
Foreground  : index 7 -> #CCCCCC
Background  : index 0 -> #0C0C0C

The full 16-entry ColorTable was also identical in both cases.

This suggests the behavior is not caused by a different Win32 console color table. The observable switch is tied to Codex detecting Windows Terminal through WT_SESSION.

Expected behavior

Codex should render consistently in Windows Terminal regardless of whether WT_SESSION is present, or terminal detection should not change the TUI background in this way.

Actual behavior

  • WT_SESSION present -> Codex detects Windows Terminal -> black rendering
  • WT_SESSION removed -> Codex reports terminal as unknown -> white rendering

The same Windows Terminal tab can be used for both cases; only the environment variable is changed.

View original on GitHub ↗

6 Comments

LazyHeidi · 10 days ago

Additional findings from testing on Codex CLI 0.147.0:

Windows Terminal is visually using a light profile (Catppuccin Latte, opacity 100%, Acrylic off), but the legacy Win32 console state exposed by GetConsoleScreenBufferInfoEx() reports:

Attributes      : 0x0007
ForegroundIndex : 7
Foreground      : #CCCCCC
BackgroundIndex : 0
Background      : #0C0C0C

With that state, launching codex produces the dark/black composer background.

If I change only the PowerShell RawUI colors before launching Codex:

$Host.UI.RawUI.ForegroundColor = 'Black'
$Host.UI.RawUI.BackgroundColor = 'White'

then GetConsoleScreenBufferInfoEx() changes to:

Attributes      : 0x00F0
ForegroundIndex : 0
Foreground      : #0C0C0C
BackgroundIndex : 15
Background      : #F2F2F2

Launching codex from the same Windows Terminal session then produces a light composer background. The Windows Terminal profile itself is unchanged.

This suggests the key mismatch is between the background actually rendered by Windows Terminal and the legacy Win32 console attributes that Codex can see. The #0C0C0C value is not the visible Catppuccin Latte background; it is the legacy console background attribute.

I also think WT_SESSION itself is still useful for detecting TrueColor capability, so simply removing the WT_SESSION -> TrueColor promotion could regress Windows Terminal color rendering. The problematic combination appears to be:

  1. WT_SESSION promotes output to TrueColor, so Codex emits an explicit RGB background.
  2. The semantic/composer background is derived from legacy Win32 console attributes that do not match the actual Windows Terminal profile background.
  3. Codex therefore explicitly paints a dark background that Windows Terminal itself is not using.

A possible fix would be to preserve the TrueColor capability promotion while avoiding use of the legacy Win32 console background as an authoritative semantic background under Windows Terminal, or otherwise distinguish terminal color capability from background-color reliability.

jdcodes1 · 9 days ago

Narrowing from main @ 1f41cc5d92 — WT_SESSION gates two coupled things, and your same-tab toggle flips both:

  1. Color level: WT_SESSION present → unconditional truecolor promotion (tui/src/terminal_palette.rs#L52-L70); absent → the supports-color result stands, typically ANSI-16 on Windows. At ANSI-16 Codex leans on the terminal's default colors (your white); at truecolor it renders explicit RGB, including painted region backgrounds.
  2. Painted backgrounds: those explicit backgrounds are derived from the startup background probe (default_bg() / set_default_colors_from_startup_probe). Styles fall back to unpainted when the probe returns None (style.rs#L35-L41), so a black full-background means the probe returned a dark value in the WT_SESSION path — most plausibly a ConPTY/OSC-11 answer that doesn't match your visible theme.

Discriminator you can run in the same tab: printf '\x1b]11;?\x07' with and without WT_SESSION set, and compare the RGB reply against what Codex paints. If the reply is your actual (light) background but Codex shows black, the probe plumbing is at fault; if the reply itself is dark, WT/ConPTY is misreporting and Codex should sanity-check probe results against rendering (or prefer Color::Reset inheritance for large fills). Either way, decoupling "terminal can do truecolor" from "paint backgrounds from probed colors" — promoting color depth without switching background strategy — would stop an env-var toggle from changing the entire theme.

LazyHeidi · 9 days ago

Thanks — the decoupling point matches what I’m seeing as well. One correction on the likely background source, though: on current main, the Windows implementation does not appear to consume OSC 10/11 for default_colors(). It explicitly uses GetConsoleScreenBufferInfoEx() instead, with the comment that reading OSC replies from the Windows console input queue can discard composer typeahead.

That lines up with the local experiment here: changing only the Win32 RawUI attributes changes GetConsoleScreenBufferInfoEx() from

0x0007 -> background index 0 -> #0C0C0C

to

0x00F0 -> background index 15 -> #F2F2F2

and Codex’s composer changes from dark to light accordingly, while the Windows Terminal profile itself remains unchanged.

So for this case, the dark semantic background seems to be coming from the legacy Win32 console buffer state rather than an OSC-11 reply. An OSC-11 check could still be useful to compare what Windows Terminal considers its real default background, but it does not look like the current Windows Codex path is using that value.

That makes the proposed separation look even more appropriate: keep WT_SESSION for truecolor capability, but avoid treating the legacy Win32 console background as authoritative for semantic/painted backgrounds when running under Windows Terminal.

tttttttime · 9 days ago

Additional 0.148.0 regression data point from native Windows Terminal + PowerShell:

  • Codex CLI 0.147.0 appeared to render the composer text correctly with the same terminal/profile configuration.
  • After upgrading to Codex CLI 0.148.0, the composer gets a dark background while typed text is also dark, making the input nearly unreadable.
  • This happens immediately at startup; no live terminal theme switch is involved.
  • Windows Terminal color scheme: iTerm2 Tango Light.
  • Relevant scheme values: foreground #000000, background #FFFFFF, ANSI black #000000.
  • Codex syntax theme: tui.theme = "inspired-github".

This version boundary suggests that 0.148.0 introduced or exposed a composer foreground/background selection regression on Windows Terminal light schemes. The terminal profile itself was unchanged between the two Codex versions.

LazyHeidi · 8 days ago

I compared rust-v0.147.0 and rust-v0.148.0, and the 0.148.0 regression appears to line up very closely with PR #38641 (Harden TUI startup input handling).

In 0.147.0, the Windows default_colors() path first queried OSC 10/11 and used the returned RGB values when available, falling back to GetConsoleScreenBufferInfoEx() only if the OSC probe failed.

In 0.148.0, the Windows OSC path was removed to avoid consuming the console input queue (and potentially losing composer typeahead), so the Windows implementation now goes directly to GetConsoleScreenBufferInfoEx().

The input-safety goal of #38641 makes sense and should probably be preserved. The problem is that under Windows Terminal, the legacy Win32 console attributes returned by GetConsoleScreenBufferInfoEx() are not necessarily the colors actually rendered by the active Windows Terminal profile.

In my environment, Windows Terminal is visibly using a light profile, while the Win32 console state reports:

background index 0 -> #0C0C0C

Changing only the RawUI/Win32 background attributes to the light side changes that value to:

background index 15 -> #F2F2F2

and Codex's composer switches from dark to light, even though the Windows Terminal profile itself is unchanged.

This also provides a plausible explanation for the new 0.148.0 report above: an environment where 0.147.0 successfully got the actual light background via OSC could become dark in 0.148.0 once the OSC path is removed and the legacy Win32 background becomes the only source.

I also checked style.rs; the composer background calculation itself is unchanged between 0.147.0 and 0.148.0. That makes the Windows default-background acquisition change look like the more likely version-boundary regression.

I would not suggest reverting #38641 and reintroducing unconditional Windows OSC reads. A safer fix seems to be to separate terminal color capability from painted-background trust:

  • Keep WT_SESSION -> TrueColor capability detection, so syntax/diff rendering does not regress.
  • Preserve the non-consuming Windows startup behavior from #38641.
  • But when running under Windows Terminal, do not treat a background sourced only from legacy GetConsoleScreenBufferInfoEx() as authoritative for large semantic/painted regions such as the composer.
  • In that case, prefer terminal-default inheritance (Color::Reset / no explicit background) rather than painting an RGB derived from the legacy console palette.

Conceptually, something like:

fn painted_region_bg() -> Option<(u8, u8, u8)> {
    #[cfg(windows)]
    if running_in_windows_terminal()
        && default_color_source() == DefaultColorSource::Win32ConsoleBuffer
    {
        return None;
    }

    default_bg()
}

with painted UI styles using painted_region_bg() instead of raw default_bg().

Longer term, it may be cleaner for DefaultColors to retain the source as well as the RGB values, e.g.:

enum DefaultColorSource {
    Osc,
    Win32ConsoleBuffer,
}

Then an OSC-derived background can continue to be used normally, while a legacy Win32 value under Windows Terminal can be treated as non-authoritative for semantic fills.

A regression test could cover the case WT_SESSION + Windows Terminal + legacy Win32 background #0C0C0C and assert that the composer does not end up with an explicit dark RGB background when that value is only coming from the legacy console buffer.

guge2 · 9 hours ago

Still present on stable codex-cli 0.150.1 (Windows Terminal 1.24.11911.0, Windows 11 10.0.26200, PowerShell 7.6.5, light theme). 0.147.0 remains the last good version in the same profile.

Opened a 0.150.1 confirmation with the 0.148 OSC-drop vs GetConsoleScreenBufferInfoEx source comparison: https://github.com/openai/codex/issues/41242

Note that #39418 was closed as completed, but rust-v0.150.1 still only uses GetConsoleScreenBufferInfoEx on Windows and never restores OSC 10/11.