[BUG] Terminal scrolls to top during agent execution (Windows Terminal + PowerShell)

Resolved 💬 16 comments Opened Mar 16, 2026 by cruzlauroiii Closed Apr 26, 2026
💡 Likely answer: A maintainer (claude[bot], contributor) responded on this thread — see the highlighted reply below.

Description

While Claude Code is executing tools/commands, the terminal viewport jumps to the topmost position of the PowerShell/terminal buffer. This happens repeatedly during agent work, forcing the user to scroll back down constantly. Expected behavior: maintain the current scroll position so the user can continue working.

Root Cause Analysis (from cli.js v2.1.76 source)

Claude Code uses Ink (React-based terminal UI) for rendering. Ink's rendering strategy:

  1. On each re-render, it moves the cursor UP to the start of the component area
  2. Erases all previous lines (eraseLines(height))
  3. Writes the new content

The core rendering code (from the bundled @inquirer/core and Ink output):

// Moves cursor up, erases, and rewrites
this.write(
  cursorDown(this.extraLinesUnderPrompt) +
  eraseLines(this.height) + 
  newContent
)

Where eraseLines is:

eraseLines = (count) => {
  let str = "";
  for (let i = 0; i < count; i++)
    str += eraseLine + (i < count - 1 ? cursorUp() : "");
  if (count) str += cursorLeft;
  return str;
}

The problem: When the output grows large (streaming responses, tool results), eraseLines(this.height) moves the cursor far up in the terminal buffer. On Windows Terminal, this cursor movement causes the viewport to follow the cursor to the top of the render area, even if the user has scrolled away.

This is a fundamental issue with Ink's diff-based terminal rendering on Windows Terminal. macOS Terminal.app and iTerm2 are also affected (per #34400, #34765).

Steps to Reproduce

  1. Start Claude Code in Windows Terminal (PowerShell)
  2. Give it a multi-step task (e.g., searching files, running commands)
  3. While it's working, scroll up to read previous output
  4. Observe: viewport snaps to top of terminal buffer on each re-render

Suggested Fix

Option A: Use alternate screen buffer for the active render area

Ink supports alternate screen buffer mode which prevents scroll interference:

// Enter alternate screen before rendering
process.stdout.write('\x1b[?1049h');
// Exit alternate screen when done
process.stdout.write('\x1b[?1049l');

Option B: Minimize re-render height

Instead of erasing the entire component and redrawing, only erase/redraw the lines that actually changed (incremental rendering).

Option C: Disable cursor movement when user has scrolled

Detect if the user has scrolled away from the bottom and suppress cursor-up movements until they scroll back.

Environment

  • OS: Windows 11 Pro 10.0.26200
  • Terminal: Windows Terminal
  • Shell: PowerShell / bash (Git Bash)
  • Claude Code: 2.1.76
  • Source: cli.js SHA-256 38b8fd29d0817e5f75202b2bb211fe959d4b6a4f2224b8118dabf876e503b50b

Related Issues

  • #34400 — Scroll position resets to top during agent execution (macOS/iTerm)
  • #34765 — Scroll position resets to top of session while processing
  • #33814 — Forces scroll to top when outputting code
  • #34052 — Scroll to top always on CLI
  • #34503 — Terminal scrolls to top on every new output
  • #33624 — Forced scroll when window gains focus (Windows Terminal + WSL2)

View original on GitHub ↗

16 Comments

cruzlauroiii · 4 months ago

Updated with v2 fix in PR #34798. The v1 approach (patching LH8/exitAlternateScreen/handleResume) only fixed edge cases. v2 injects a global stdout.write interceptor that clamps cursor-up across ALL writes, catching the primary sources: Ink's hV7() clear function and the Inquirer prompt renderer.

cruzlauroiii · 4 months ago

Updated PR #34798 with 3 combined patches: (1) stateful stdout.write interceptor clamping cursor-up, (2) render throttle 16ms→200ms, (3) disable synchronized update mode. All three are needed — cursor-up clamping alone does not fix it because Windows Terminal exits scroll mode on ANY stdout write, and sync blocks batch writes into one viewport-resetting update. PowerShell apply/revert script included.

cruzlauroiii · 4 months ago

v3 update: Previous fixes (cursor-up clamping, disabling sync update) did not work and caused flickering.

Root cause identified: Windows Terminal bug microsoft/terminal#14774SetConsoleCursorPosition always scrolls viewport to cursor, even when visible. Every Ink re-render triggers this. Cannot be fixed from within the process.

v3 patch: render throttle SK6=16SK6=1000 (1fps). Gives 1 second of uninterrupted reading between viewport resets. Trade-off: streaming text in ~1s chunks. No flickering.

PR #34798 updated. Real fix needs Microsoft (terminal#14774) or Anthropic (PTY proxy/append-only rendering).

cruzlauroiii · 4 months ago

v5 update: Fundamentally different approach — buffer ALL Ink renders during active work, screen stays frozen (no viewport jumping). Flush only when (a) user types (at prompt/bottom, safe to update) or (b) 5s of no renders (Claude finished). Previous v1-v4 failed because WT bug microsoft/terminal#14774 triggers viewport scroll on ANY cursor positioning. v5 avoids this by not writing to stdout at all during active work. Trade-off: no spinner/progress visible during work. PR #34798 updated.

cruzlauroiii · 4 months ago

v6: Buffer ALL renders, flush ONLY on user input (stdin). No timer. No auto-flush. Zero viewport jumping while not at bottom. Root cause: WT bug microsoft/terminal#14774. PR #34798.

cruzlauroiii · 4 months ago

v7: Replay ALL buffered frames on flush (fixes v6 outdated screens). Keeps entire diff chain so screen content is always correct. Still zero viewport jumping — flush only on user input (stdin). PR #34798.

cruzlauroiii · 4 months ago

Final fix: Ctrl+6 freeze toggle. Press Ctrl+6 to freeze screen (buffer renders, scroll freely). Press again to unfreeze (replay + live output). Tab title shows [FROZEN]. Root cause is WT bug microsoft/terminal#14774 — cannot be detected/fixed automatically. PR #34798.

cruzlauroiii · 4 months ago

A fix for this is available as a Claude Code plugin: https://github.com/anthropics/claude-code/pull/35683

The scroll-fix plugin clamps cursor-up sequences within synchronized output blocks to the viewport height, preventing the terminal from scrolling to the top. Also includes Ctrl+6 freeze toggle for manual scroll control. Works on all platforms and terminals.

cruzlauroiii · 4 months ago

A fix is available as a Claude Code plugin: scroll-fix

Install:

/plugin marketplace add cruzlauroiii/claude-code
/plugin install scroll-fix@cruzlauroiii-plugins

Root cause: both Ink renderer AND readline/prompt system emit cursor-up sequences exceeding viewport height. The plugin clamps all cursor-up per write call. Also includes Ctrl+6 freeze toggle.

PR: https://github.com/anthropics/claude-code/pull/35683

nullbio · 4 months ago

This is like the 10th time this has regressed.

HomeHeartTherapy · 3 months ago

+1 — This is a significant productivity blocker.

Environment:

  • Windows 11 Pro (10.0.26200)
  • Windows Terminal 1.23.20211.0
  • Shell: Git Bash (primary), PowerShell 7.6.0
  • Claude Code: latest (Opus 4.6, 1M context)

Impact: During any multi-step task, scrolling up to read prior output while Claude works is impossible — the viewport snaps away on every Ink re-render cycle. This forces a "wait until it's done" workflow that kills the ability to review work in progress, catch issues early, or stay oriented during long operations.

What I've tried:

  • "snapOnOutput": false in Windows Terminal profile defaults — no effect on the primary issue (cursor-repositioning jumps)
  • The setting only addresses output-triggered scrolling, not the eraseLines(height) cursor-up sequences from Ink's renderer

What works: tmux (full scroll isolation), but that requires running Claude Code inside WSL, which changes the entire workflow and breaks native Windows hooks/paths.

What would actually fix this: Anthropic replacing Ink's full-erase re-render strategy with incremental rendering (only redraw changed lines) or using the alternate screen buffer for the active render area. The root cause is in Claude Code's renderer, not in Windows Terminal — though WT bug microsoft/terminal#14774 compounds it.

This is the #1 UX issue preventing efficient use of Claude Code on Windows Terminal.

aguestuser · 3 months ago

Also seeing this on iTerm2 (macOS, Darwin 25.3.0). The viewport jumps to the top of the scroll buffer during tool execution and TUI redraws. Tried toggling several iTerm2 settings (scroll to bottom on output, save lines to scrollback with status bar present, alternate mouse scroll) — none resolved it.

FurbySoup · 3 months ago

Been following this thread and built something to address it — sharing in case it helps people while waiting for an upstream fix. Genuinely curious whether it helps people's specific setups.

Quell is a Windows terminal built specifically for Claude Code. It intercepts VT output via ConPTY and strips the sequences responsible for the scroll-jumping before they ever reach the renderer.

What it fixes (as of v0.2.1):

All three root causes now handled:

  • ESC[3J — erase scrollback — stripped unconditionally
  • ESC[2J — erase display — stripped outside DEC 2026 synchronized update blocks (allowed inside, where it's part of an atomic redraw)
  • ESC[nA — cursor-up — stripped outside sync blocks (v0.2.1, shipped two days ago)

The sync-block awareness matters for the latter two: Ink wraps its redraws in BSU/ESU markers, so we can distinguish "this is a legitimate UI repaint" from "this is orphaned cursor movement that will snap the viewport." The sed patch approach strips cursor-up unconditionally — which works, but also strips legitimate cursor movement. Our approach is more surgical.

Two ways to use it:

GUI (recommended — eliminates the problem entirely): A standalone terminal app — download the installer. Because we own the renderer (xterm.js), the cursor-up issue has nowhere to propagate to. This is the cleanest fix.

CLI proxy: For people who want to stay in their current terminal — quell claude wraps Claude Code in the filter layer. Handles ESC3J and ESC[2J completely. Cursor-up handling helps significantly, though in very long sessions you may still see minor drift from [microsoft/terminal#14774 — that's an upstream terminal behaviour we can mitigate but not fully control from a proxy position.

What I'd genuinely like to know:

Does this actually solve it for people's real sessions? The cases I'm most uncertain about are very long sessions (1000+ line scrollback) and heavy tool-use sequences where Ink redraws rapidly. If anyone tries it and finds cases where the jumping still occurs, I'd like to understand exactly what's happening — logs, repro steps, whatever's useful.

Also — now that Claude Code is 'open source', I'm looking at whether proposing that Ink adopt sync markers more consistently is viable. That would make the proxy approach essentially lossless for both CLI and GUI users. If anyone's interested in that angle, happy to discuss.

Windows only for now (ConPTY is the core of the fix). MIT licensed, no telemetry.

parsakhaz · 3 months ago
this is a huge issue. love claude code but this makes it really really hard to use

hey. it's fixed at the terminal level here

https://github.com/Dcouple-Inc/Pane/pull/120

open source, using xtermjs which is the same terminal as VS Code. feel free to copy this fix or download the latest release to run claude code on any operating system with the fix in place. i was so mad and frustrated at this that I had to fix it myself.

Not just a scrolling issue — also a rendering/layout problem in VS Code's side-by-side panel mode. When Claude Code runs in VS Code with the terminal on one side and the editor on the other, text formatting breaks in unpredictable ways. Options and multi-line output get weirdly split left-right, text wraps incorrectly, and the whole experience feels janky rather than fluid. Combined with the scroll-jumping, longer sessions in VS Code become genuinely hard to follow. This isn't just macOS standalone terminal — it's very much a VS Code integrated terminal issue too.

yeah i had to switch off cursor/vscode and build myself an ide that managed xtermjs (the library they both use) well enough for the complex TUIs to not break.

works great and i have a fix for the scroll jump i just implemented rn here

https://github.com/Dcouple-Inc/Pane/pull/120

claude[bot] contributor · 2 months ago

This is a duplicate of #35403, which was fixed as of version 2.1.101.

github-actions[bot] · 2 months ago

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.