openai / openai/codex

Windows: resumed user messages render without their background because the transcript is styled before the deferred default-color probe

Open
#43,837 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug CLI session TUI windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of Codex CLI is running?

codex-cli 0.153.4

What subscription do you have?

Pro

Which model were you using?

gpt-6-astra (xhigh)

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64 (Windows 11 Pro)

What terminal emulator and version are you using (if applicable)?

Reproduced in two hosts: PowerShell 7 in its own console window, and Runner (a GPUI app that embeds alacritty_terminal over ConPTY / OpenConsole 1.24.260303001). No multiplexer.

Codex doctor report
not run
What issue are you seeing?

On Windows, after codex resume, the user messages replayed from the resumed transcript render as plain text with no background block, while the composer at the bottom draws its gray block as usual. Messages sent after the resume get the block. Any change of the terminal width makes the resumed messages get their block back. This is Windows-only by construction: on Unix, init() runs the default-color probe synchronously before anything is drawn.

Root cause (verified against rust-v0.153.4 sources and a ConPTY capture; details below): on Windows the default-color probe is deferred until after protected startup screens, but the resumed transcript is styled before that probe runs, so user_message_style() sees default_bg() == None and emits no background. Those lines are written to scrollback once and never restyled; the composer is restyled every frame, so it picks the color up as soon as the probe completes.

Where it happens

  • tui/src/tui.rs:503 — on Windows, init() calls set_default_colors_from_startup_probe(None) on purpose ("OSC replies can arrive after their deadline. Do not issue terminal queries before directory trust and other protected startup screens have finished..."). The real probe is probe_default_colors_after_protected_startup().
  • tui/src/style.rs:65-70user_message_style_for(None) returns Style::default(), i.e. no background at all (not a fallback color).
  • tui/src/app/startup.rs:634drain_active_thread_events replays the resumed thread; each cell goes through insert_history_cell (tui/src/app/history_ui.rs:24), which renders the display lines right away via display_hyperlink_lines_for_mode (history_ui.rs:35, resize_reflow.rs:87). That is where user_message_style() is evaluated, with the palette still None.
  • tui/src/app/startup.rs:657 — only after the drain does probe_default_colors_after_protected_startup() run.
  • tui/src/app/resize_reflow.rs:191 (insert_history_cell_lines_with_initial_replay_buffer) keeps the already-styled lines in retained_lines; finish_initial_history_replay_buffer (resize_reflow.rs:156) writes them to the terminal later. So the bytes reach the terminal after the OSC reply, but they were styled before it.

Timeline from Codex's own log (a resume inside Runner; logs_2.sqlite):

+0.000  INFO  codex_tui::app::startup   tui startup initial frame scheduled duration_ms=3933 bootstrap_ms=3633 thread_and_widget_ms=185 initial_session_ms=113   (tui/src/app/startup.rs:668)
+0.105  DEBUG codex_tui::terminal_probe::imp  using Windows terminal OSC default colors foreground=(220, 220, 224) background=(21, 22, 27)   (tui/src/terminal_probe/windows.rs:43)
+0.105  INFO  codex_tui::tui   terminal default color probe completed duration_ms=0 default_colors=true   (tui/src/tui.rs:536)

The terminal answered the query instantly (duration_ms=0); the probe simply ran after the transcript had been styled.

Byte-level capture (real codex.exe under ConPTY in a small harness that models the output with alacritty_terminal and answers OSC 10/11 in the same read; env TERM=xterm-256color, COLORTERM=truecolor, 149x49; resuming a thread whose only user message is zsdfsadf):

t event
279 ms Codex writes › zsdfsadf — the chunk contains no 48;2; SGR
283 ms Codex queries OSC 10;? / OSC 11;?; harness replies rgb:1515/1616/1b1b in the same millisecond
4013 ms harness resizes the PTY 149 → 120 columns
4093 ms resize reflow re-emits the transcript; › zsdfsadf now arrives with SGR 48;2;49;49;54 (the 12% white blend of the reported background) as a full three-row block

Final grid before the resize: the resumed message row has the default background; the composer rows have rgb(49,49,54). After the resize both do.

What steps can reproduce the bug?
  1. On Windows (PowerShell 7 console or Windows Terminal), run codex, send any message such as hello, wait for the reply, exit.
  2. Run codex resume --last.
  3. Look at the replayed › hello line: no background block. The composer below it has the block.
  4. Send a new message: it gets the block.
  5. Resize the window width by one column: the replayed › hello now has the block too.

Thread used for the capture above: 01a08124-acf3-7691-a4f2-d47a955b64eb.

What is the expected behavior?

Resumed user messages render with the same background as the composer and as newly sent messages, matching macOS/Linux.

Additional information

Possible fixes, from what the code already has:

  1. After probe_default_colors_after_protected_startup() sets real colors, re-render the transcript from transcript_cells if any history was styled before the probe. schedule_immediate_resize_reflow already does exactly this on width changes (that is what step 5 above exercises), so gating a one-time reflow on "probe completed after history was emitted" would fix it with existing machinery.
  2. Alternatively, when the Windows probe is still pending during the initial replay, set render_from_transcript_tail on the initial replay buffer so finish_initial_history_replay_buffer goes through the reflow path (styling from cells after the probe) instead of writing lines that were styled with None.
  3. Or run the probe before drain_active_thread_events whenever ready_for_terminal_color_probe is already true at that point (no protected request queued), keeping the deferral only for the cases the comment in tui.rs:503 is about.

Not related to the terminal's reply latency: the capture above answers within the same millisecond and the log reports duration_ms=0, and the defect reproduces the same way in the stock PowerShell 7 console.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Trace the Windows startup flow in tui/src/tui.rs and tui/src/app/startup.rs, then follow resumed history through history_ui.rs and resize_reflow.rs. Reproduce with codex resume on Windows and inspect when user_message_style() runs relative to probe_default_colors_after_protected_startup(). Done means resumed messages receive the same background as the composer without requiring a terminal resize.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.