Skip to content

Root cause + fix for scroll jumping on long responses (CSI 2J inside DEC 2026 sync blocks) #35580

Closed as duplicate

Description

@inchwormz

Problem

When Claude Code streams long responses (400+ lines), the terminal scroll position resets to the top of the visible output on every render update. You can't read earlier output while new content arrives. This is the most upvoted UX issue across AI CLI tools (see #1208, #656, #548).

Root cause

Claude Code uses DEC 2026 synchronized output (BSU/ESU markers) to batch screen updates atomically. Good.

The problem: when a response exceeds the visible terminal height, Claude Code emits a full-screen redraw inside the sync block:

BSU (begin sync)
  CSI H        ← cursor home (move to 0,0)
  CSI 2J       ← erase entire display
  [full content re-render]
ESU (end sync)

CSI 2J (erase display) resets the terminal's scroll position. The terminal processes the entire sync block atomically, so the scroll position snaps to the top after every update.

Short responses use incremental updates and don't include CSI 2J. They scroll fine. The bug only fires on full redraws.

Fix

Strip CSI 2J and the leading CSI H from sync blocks that contain a full-screen redraw. Content still renders correctly because cursor positioning sequences within the content handle placement. The BSU/ESU wrapper stays intact, so the terminal still processes the update atomically.

Detection logic:

  1. Parse output for DEC 2026 sync block boundaries (BSU/ESU)
  2. Check if the sync block contains both CSI 2J (erase display) and CSI H (cursor home). That's the full-redraw signature.
  3. If yes: strip CSI 2J entirely, strip CSI H only at position 0 (not mid-content), re-wrap in BSU/ESU
  4. If no: pass through unmodified, preserving original timing

Important: normal sync blocks (incremental updates) must pass through untouched. Intercepting all sync blocks breaks timing and causes a different scroll regression.

Working implementation

FurbySoup/quell is an open-source Windows ConPTY proxy that implements this fix. Tested against Claude Code:

  • 398-line single response: no scroll jumping
  • 5 back-to-back responses (100-200 lines each): scroll position held while scrolling between responses
  • Short, medium, long, code-heavy, mixed content: all pass

Relevant code:

The fix is ~50 lines of logic. The same approach works natively in Claude Code's rendering pipeline, removing the need for an external proxy.

Suggested integration path

Option A: stop emitting CSI 2J during streaming updates. Use cursor positioning (CSI <row>;<col>H) and selective line clearing (CSI 2K) to update only changed regions. This is how htop and vim avoid scroll jumping during full-screen updates.

Option B: if full redraws are architecturally required, strip the CSI 2J before writing to stdout when inside a sync block. Content renders correctly without it.

Environment

  • Windows 11, Windows Terminal 1.25+
  • Claude Code latest
  • Also reproduced on macOS Terminal and iTerm2 (same VT sequence pattern)

References

Activity

  1. github-actions commented on Mar 18, 2026

    @github-actions

    Found 3 possible duplicate issues:

    1. [BUG] Terminal scrolls to top during agent execution (Windows Terminal + PowerShell) #34794
    2. [BUG] Forces scroll to top when outputting code (NOT just when scrolling) #33814
    3. [Bug] Terminal auto-scrolls to buffer start when model writes output during scroll-up #35415

    This issue will be automatically closed as a duplicate in 3 days.

    • If your issue is a duplicate, please close it and 👍 the existing issue instead
    • To prevent auto-closure, add a comment or 👎 this comment

    🤖 Generated with Claude Code

  2. self-assigned this
    on Mar 19, 2026
  3. github-actions commented on Mar 21, 2026

    @github-actions

    This issue has been automatically closed as a duplicate of #34794.

    If this is incorrect, please re-open this issue or create a new one.

    🤖 Generated with Claude Code

  4. github-actions commented on Mar 29, 2026

    @github-actions

    This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.

  5. locked as resolved and limited conversation to collaborators on Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

duplicateThis issue or pull request already exists

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions