Renderer corrupts screen in long iTerm2 sessions — CLI emits cursor-up sequences far larger than the viewport, repaint anchors to row 1 (regression after 2.1.162, still present in 2.1.177)

Open 💬 6 comments Opened Jun 14, 2026 by reamit

Summary

In long-running / resumed sessions on macOS iTerm2, the TUI renderer progressively corrupts the screen: the prompt jumps to the top of the viewport and stale content below is slowly overwritten on subsequent repaints. A full redraw (Ctrl+L) recovers temporarily, then it recurs. Short / fresh sessions are unaffected.

Root cause is captured below from a raw script tty log: the CLI itself emits cursor-up escape sequences far larger than the terminal height (up to ESC[712A), which clamp at the top of the screen and cause the next repaint to start at row 1. The capture is forensically dispositive and I can attach it.

Environment

  • Claude Code versions:
  • Clean: 2.1.140 (long historical use), 2.1.162 (real-world use, no corruption observed over a full working day).
  • Corrupting: 2.1.170 and 2.1.173 (forensic capture below), and 2.1.177 (a live session running 2.1.177 reproduced the corruption; not separately captured).
  • 2.1.169: ambiguous, not conclusively classified.
  • So the regression was introduced somewhere in 2.1.163–2.1.170 and persists through the latest build (2.1.177). The exact introducing build is not yet bisected.
  • OS: macOS (Darwin 25.4.0)
  • Terminal: iTerm2 3.6.9
  • Renderer: default TUI renderer (tui: default in settings, which uses the terminal's alternate screen buffer)
  • TERM=xterm-256color, no tmux
  • Aggravators present in this setup (not confirmed as causes): long resumed sessions, a custom statusline, and the remote-control footer (a persistent footer line in the TUI)

Root cause (from raw tty capture)

I captured the renderer output with script while corruption was occurring. The CLI emits cursor-up escape sequences far larger than the terminal can hold:

  • ESC[<N>A with N ranging from 101 up to 712.
  • 3918 such oversized moves in a single capture, out of 14843 cursor-up moves total.
  • For reference, the largest cursor-up observed in a clean (non-corrupting) session is 59 rows. Any move past the terminal height is illegitimate: the terminal clamps the cursor at row 1 (it cannot scroll above the top of the screen), so the subsequent repaint begins at row 1 and overwrites on-screen content.
  • Also observed: width desync — the renderer paints at a stale, narrower width (text breaks one word per line), suggesting the renderer's internal column/row model has drifted from the terminal's actual size.

This is consistent with the renderer tracking a growing assistant response as its repaint frame, computing a cursor-up larger than the physical screen, and anchoring the repaint to the top instead of the correct row.

Anyone can reproduce the analysis on the attached capture with:

perl -ne 'while(/\e\[(\d+)A/g){$t++; $b++ if $1>=100} END{printf "%d cursor-up total, %d exceed 100 rows\n",$t,$b}' capture.log

Steps to reproduce

  1. Resume a long session in iTerm2 on 2.1.170+ (default TUI renderer).
  2. Use it through several streamed responses. A monitor resize / display change while a response is rendering appears to trigger onset.
  3. The screen begins jumping to the top with stale text overwritten below.

Reproduction caveat: a fresh session reproduces poorly. We were unable to reproduce with any zero-token synthetic trigger on a known-bad build — idle resize storms, paste storms, transcript-viewer scrolling, and bash-mode local streaming, each combined with window resizes, in both detached ptys and a real iTerm2 window. All stayed clean (max cursor-up 59, i.e. correct). Reproduction appears to require real model streaming (a growing assistant message) plus accumulated long-session renderer state.

Not terminal-specific

This was only observed in iTerm2, but the captured sequences (ESC[712A) would corrupt any terminal that clamps cursor-up at row 1, which is standard VT behavior. The fault is in the emitted sequences, not in iTerm2's handling. Other terminals (Terminal.app, kitty) were not tested.

Workarounds tried

  • CLAUDE_CODE_FORCE_SYNC_OUTPUT=1 — insufficient, recurs.
  • Remote control off — insufficient, recurs.
  • Pin to a pre-regression build (2.1.140, or 2.1.162) — resolves it.

Impact

Sessions become unusable without repeated Ctrl+L. The only reliable fix is to downgrade and pin to an old build, which blocks adoption of current releases.

I have the raw script capture with the corruption in the act (≈1.9 MB) and can attach it on request.

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗