Differential renderer uses CUF (cursor forward) instead of spaces, causing stale cell content to show through

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 18, 2026

Description

Claude Code's TUI renderer sometimes uses CSI n C (CUF — Cursor Forward) instead of actual space characters when rendering spaces between words. CUF is a non-destructive cursor movement per the VT100/ECMA-48 spec — it moves the cursor without modifying cell content. This causes stale characters from previous render frames to show through at positions where spaces should appear.

Reproduction

The issue manifests in scrollback content captured from Claude Code's output. For example, a "Wrote 44 lines to ..." result line renders as:

Expected: Wrote 44 lines to Tests\...\CommonMarkConformanceTests.cs
Actual: Wrotei44…lines5to Tests\...\CommonMarkConformanceTests.cs

The garbled characters (i, , 5) are remnants from a prior render frame that occupied the same screen positions.

Root cause analysis

Comparing the raw ANSI output for the same type of content shows two different rendering strategies:

Correct rendering (uses actual space characters):

Wrote \e[1m400\e[22m lines to \e[1mRaisinDocs\MinimapScrollbar.cs\e[22m

Garbled rendering (uses CUF instead of spaces):

Wrote\e[1m\e[1C44\e[22m\e[1Clines\e[1Cto\e[1m\e[1CTests\...\CommonMarkConformanceTests.cs\e[22m

Every \e[1C (CUF 1) in the garbled version corresponds to a space in the correct version. CUF does not write to the cell — it only repositions the cursor. Per ECMA-48 and the terminfo(5) documentation:

"These local cursor motions should not alter the text they pass over, for example, you would not normally use cuf1= because the space would erase the character moved over."

When the cell at a CUF-skipped position previously held a non-space character from an earlier render frame, that character remains visible.

Evidence

  • The same content is rendered with actual spaces during full redraws but with CUF during incremental updates
  • Resizing the terminal window fixes the corruption (forces a full redraw where all cells are explicitly written)
  • Selecting text does not fix it (selection is a visual overlay; the underlying cell buffer retains stale content)
  • The garbled text is captured into scrollback with the stale characters, making it permanent in the scroll history

Environment

  • Claude Code v2.1.183, Opus 4.6
  • Windows 11 Pro, ConPTY
  • Custom terminal emulator (RaisinTerminal) — CUF implemented per VT100 spec (non-destructive cursor movement)
  • The issue is in the VT output generated by Claude Code's renderer, not in the terminal's interpretation of it

Related issues

  • #59539 — Terminal display corrupted with garbled characters
  • #19637 — Windows cmd text overlapping and garbled display
  • #59915 — Rendering corruption fixable by selecting text (forcing repaint)
  • #68711 — Text display corruption resolved by window resize (forcing full redraw)

Suggested fix

The differential renderer's cell-level diffing should detect cells that transition from non-blank to blank and emit explicit space characters (or use EL — Erase in Line) for those positions, rather than skipping them with CUF. CUF should only be used to skip cells whose content is genuinely unchanged between frames.

View original on GitHub ↗