--no-alt-screen still does not preserve scrollback in xterm.js-based terminals (VS Code integrated terminal reproduces)`

Open 💬 5 comments Opened Mar 11, 2026 by imsuperli
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

0.111.0

What subscription do you have?

Plus

Which model were you using?

gpt-5.4

What platform is your computer?

windows 11 x64

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

Windows Terminal (powershell)

What issue are you seeing?

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

Reproduces in:

  • VS Code integrated terminal
  • My own Electron app embedding xterm.js

Does not reproduce in:

  • Windows Terminal + PowerShell 7

### What issue are you seeing?

When I run codex --no-alt-screen, once the conversation exceeds one screen, I still cannot scroll back to earlier content in xterm.js-based terminals. I can only see the currently visible screen contents.

I also reproduced the same issue in the VS Code integrated terminal, so this does not appear to be specific to my own Electron app.

This makes --no-alt-screen hard to use as a practical workaround for terminal scrollback, because disabling the alternate screen does not actually preserve the prior conversation in these environments.

Behavior summary:

  • In Windows Terminal + PowerShell 7, I can scroll back normally.
  • In xterm.js-based terminals (my Electron app and VS Code integrated terminal), I cannot.

Based on discussion in xtermjs/xterm.js#5745, an xterm.js maintainer suggested that Codex may be explicitly clearing the visible screen and purging scrollback on the normal buffer (for example via CSI
2J
/ CSI 3J) instead of only avoiding the alternate screen. If that analysis is correct, then --no-alt-screen still does not provide native scrollback in terminals that implement those control sequences
strictly.

This seems related to earlier Codex issues around scrollback / alternate screen, but not fully solved by --no-alt-screen.

What steps can reproduce the bug?

  1. Open a VS Code integrated terminal on Windows.
  2. Run codex --no-alt-screen.
  3. Have a conversation long enough to exceed one screen.
  4. Try to scroll up to view earlier conversation content.
  5. Observe that only the current screen contents are available.
  6. Repeat the same test in an Electron app using xterm.js and observe the same behavior.
  7. Repeat the same test in Windows Terminal + PowerShell 7 and note that scrollback works there.

What is the expected behavior?

codex --no-alt-screen should preserve earlier conversation output in the terminal's normal scrollback buffer, so users can scroll up and review previous content in terminals that support native scrollback.

At minimum, --no-alt-screen should avoid behavior that clears or purges normal-buffer scrollback.

Additional information

I already opened an xterm.js issue here:
https://github.com/xtermjs/xterm.js/issues/5745

In that thread, an xterm.js maintainer suggested the current behavior may be caused by Codex explicitly issuing clear / purge sequences on the normal buffer, and that stricter terminals may therefore be
behaving as designed.

If that analysis is correct, the request here is not "please change xterm.js", but rather one of the following on the Codex side:

  • make --no-alt-screen a true scrollback-preserving mode in the normal buffer, or
  • provide a separate inline / append-only renderer mode that does not rely on full-screen redraw plus clear/purge behavior, or
  • clarify in the documentation that --no-alt-screen does not guarantee usable native scrollback in strict terminals.

Potentially relevant Codex source reference discussed in the xterm.js thread:
https://github.com/openai/codex/blob/2bc3e52a91bb88a0e067a95f8f8559f8711d30e6/codex-rs/tui/src/custom_terminal.rs#L469

I can provide logs or a minimal reproduction if that would help.

参考

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 5 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #12457
  • #13443

Powered by Codex Action

jimbb · 4 months ago

I investigated this locally and pushed a proposed fix branch here:

https://github.com/jimbb/codex/tree/fix-inline-scrollback-safe-mode

Compare view:

https://github.com/openai/codex/compare/main...jimbb:fix-inline-scrollback-safe-mode?expand=1

I also tried to open a PR against openai/codex, but GitHub rejected CreatePullRequest for this account/repo combination, so I am linking the pushed branch directly.

Root cause here is that disabling alternate screen is not enough on its own. Outside the existing Zellij-specific branch, inline history insertion and viewport growth still use the DECSTBM / reverse-index path, which can drop or stale out global scrollback in xterm.js-style terminals on Windows.

This patch reuses the already-existing newline-based safe path, generalizes it to ScrollbackSafe, and applies it to the same inline-history failure mode beyond Zellij.

Related:

  • #12457
  • #14277
  • #13443 (duplicate of #12457)
  • #11192 (closed prior fix attempt on the same renderer path)
jimbb · 4 months ago

Given the current contributing policy says external PRs are by invitation only, would you prefer that I open this as an invited PR, or would you rather cherry-pick/take the branch directly?\n\nCompare: https://github.com/openai/codex/compare/main...jimbb:fix-inline-scrollback-safe-mode?expand=1\nCommit: 72151d628\n\nHappy to adjust the patch if this direction is close but not quite what you want.

parsakhaz · 4 months ago

hey, hit the same class of bug in Pane (my cross-platform terminal-first ai code assistant manager for running claude code / codex in parallel w/ git worktree isolation). xtermjs resets viewportY on every \x1b[2J, even inside a DEC 2026 sync block, so any full-redraw from codex yanks the viewport around mid-stream.

if youre building your own electron + xtermjs wrapper like OP: fix is to filter \x1b[2J at the PTY->terminal boundary when inside a \x1b[?2026h / \x1b[?2026l block. track sync state across chunks so you dont drop ED2 thats meant for regular clears. every other TUI (vim, htop, clear) is unaffected. https://github.com/Dcouple-Inc/Pane/pull/120

root issue is upstream in xtermjs tho. PR #5453 added DEC 2026 support for 6.0 but only implemented the "defer rendering" half, the "suppress viewport side-effects of ED inside BSU/ESU" half wasnt touched. just filed a dedicated xtermjs issue: xtermjs/xterm.js#5801

NoNine · 3 months ago

Adding another reproduction from Linux with Codex CLI 0.133.0.

In this environment, --no-alt-screen appears to be accepted by the CLI but does not produce a visible behavior change in the interactive TUI.

Version/environment:

codex-cli 0.133.0
Linux x86_64
TERM=linux in the shell
NO_COLOR=1
TMUX unset
Also tested under a pty with TERM=xterm-256color

I captured startup output for both forms with script and scanned for standard alternate-screen enter/leave sequences:

script -q -e -c 'TERM=xterm-256color timeout --signal=INT 1s codex' /tmp/codex-alt.typescript
script -q -e -c 'TERM=xterm-256color timeout --signal=INT 1s codex --no-alt-screen' /tmp/codex-noalt.typescript

perl -0ne 'print "enter1049=".(()=/\e\[\?1049h/g)." leave1049=".(()=/\e\[\?1049l/g)." enter47=".(()=/\e\[\?47h/g)." leave47=".(()=/\e\[\?47l/g)." bytes=".length($_)."\n"' /tmp/codex-alt.typescript
perl -0ne 'print "enter1049=".(()=/\e\[\?1049h/g)." leave1049=".(()=/\e\[\?1049l/g)." enter47=".(()=/\e\[\?47h/g)." leave47=".(()=/\e\[\?47l/g)." bytes=".length($_)."\n"' /tmp/codex-noalt.typescript

Observed:

default: enter1049=0 leave1049=0 enter47=0 leave47=0 bytes=7446
noalt:   enter1049=0 leave1049=0 enter47=0 leave47=0 bytes=7462

So in this setup the default path is already not emitting the usual alternate-buffer sequences, and --no-alt-screen has nothing visible to change. The TUI still clears/redraws the normal screen, so the practical outcome is the same as described in this issue: --no-alt-screen does not act as a true scrollback-preserving/append-only mode.

One related config detail: this build rejects boolean config values for tui.alternate_screen at TUI startup:

[tui]
alternate_screen = false

The accepted values appear to be string enums, e.g.:

[tui]
alternate_screen = "never"

Testing "always", "never", and "auto" in the same pty did not produce ?1049h/l either. This suggests the current option/documentation may be controlling only one part of the rendering path, while the full-screen repaint behavior that breaks native scrollback remains.