TUI resize reflow emits full clear outside synchronized output, causing blank flash

Open 💬 2 comments Opened Jun 1, 2026 by h3nock

What version of Codex CLI is running?

codex-cli 0.135.0

Also reproduced earlier with 0.134.0.

What subscription do you have?

Not subscription-specific.

Which model were you using?

Not model-specific, this is TUI resize/rendering behavior.

What platform is your computer?

macOS 26.6.0 arm64 / Apple Silicon.

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

Reproduced in multiple terminal frontends, including:

  • Ghostty 1.3.1 (TERM=xterm-ghostty)
  • iTerm2

The same underlying byte ordering is visible in raw terminal-output captures, so this does not appear specific to one terminal frontend.

What issue are you seeing?

During terminal resize reflow, Codex emits a full terminal clear outside the synchronized output transaction that contains the repaint. Terminals that honor synchronized output can briefly present the cleared screen before the repaint arrives, producing a visible blank flash during resize.

The relevant bad ordering is:

ESC[?2026l
ESC[r ESC[0m ESC[H ESC[2J ESC[3J ESC[H
ESC[?2026h ...replay/repaint...

That is:

sync end -> full clear -> next sync start/repaint

The bug is not tied to one terminal. The reproducible condition is a long Codex TUI session plus a terminal height resize that triggers resize reflow.

What steps can reproduce the bug?

Manual visual repro:

  1. Open Codex in a normal terminal.
  2. Resume a long session with enough visible/replayable content.
  3. Resize the terminal window shorter/taller a few times.
  4. Affected builds briefly flash a blank cleared screen during resize.

Byte-level proof:

Capture Codex's terminal output while performing the resize, then inspect the ordering of synchronized-output markers and the resize-replay full clear:

  • synchronized output start: ESC[?2026h
  • synchronized output end: ESC[?2026l
  • resize-replay full clear: ESC[r ESC[0m ESC[H ESC[2J ESC[3J ESC[H

The affected ordering is:

ESC[?2026l
ESC[r ESC[0m ESC[H ESC[2J ESC[3J ESC[H
ESC[?2026h ...

The exact terminal size is not important. The important condition is a long TUI session plus a height resize that causes resize reflow.

What is the expected behavior?

Resize replay should present clear + replay/repaint as one synchronized output transaction:

sync start -> full clear -> replay/repaint -> sync end

Terminals that honor synchronized output can then avoid publishing the intermediate cleared state.

Additional information

Related but not duplicate:

  • #21978 tracks stale terminal size / constrained rendering after resize in Ghostty and is still open. It does not identify the synchronized-output atomicity failure or provide raw byte ordering.
  • #18575 introduced terminal resize reflow and fixed older stale wrapping / scrollback issues. The current bug appears to be an atomicity gap in that resize-reflow path, not evidence that the reflow feature is absent.

Root-cause hypothesis from source inspection:

codex-rs/tui/src/app/resize_reflow.rs clears immediately in clear_terminal_for_resize_replay():

fn clear_terminal_for_resize_replay(&mut self, tui: &mut tui::Tui) -> Result<()> {
    if tui.is_alt_screen_active() {
        tui.terminal.clear_visible_screen()?;
    } else {
        tui.terminal.clear_scrollback_and_visible_screen_ansi()?;
    }
    let mut area = tui.terminal.viewport_area;
    if area.y > 0 {
        area.y = 0;
        tui.terminal.set_viewport_area(area);
    }
    Ok(())
}

Then draw_with_resize_reflow() later wraps viewport/history/render in stdout().sync_update(...). That means the clear can leave the process before synchronized output is enabled for the replay/repaint.

Codex doctor report

codex doctor --json was run under a real pty and reported overallStatus: ok. Full output is omitted here because it contains local paths and environment details that are not needed for this rendering bug.

Relevant fields:

{
  "overallStatus": "ok",
  "codexVersion": "0.135.0",
  "runtime.provenance": {
    "platform": "macos-aarch64",
    "version": "0.135.0"
  },
  "terminal.env": {
    "status": "ok",
    "TERM": "xterm-256color",
    "stdin/stdout/stderr": "terminal"
  },
  "updates.status": {
    "latest version": "0.135.0",
    "latest version status": "current version is not older"
  }
}

View original on GitHub ↗

2 Comments

furqan-debug · 2 months ago

Hi, I'd like to investigate and work on this issue. I'll try reproducing it and validate whether moving the resize replay clear operation into the synchronized output transaction resolves the blank flash.

furqan-debug · 2 months ago

I investigated this issue and implemented a candidate fix.

The approach defers terminal clearing until the synchronized update/render path executes, preventing the blank cleared frame from being emitted before replay/repaint.

Implementation:
furqan-debug/codex
branch: fix/issue-25622-resize-reflow-race-condition
commit: c66e8cf

Unfortunately it appears external PR creation is currently restricted, so I'm sharing the implementation details here in case it's useful.