Skip to content

Virtual scroll height underestimation makes older messages unreachable in long/resumed sessions #42076

@AutumnsGrove

Description

@AutumnsGrove

Summary

In long conversations or resumed sessions, older messages become permanently unreachable via scrolling. The scrollable range shrinks progressively as new tall messages arrive, eventually limiting visible history to only the most recent ~5 messages.

Reproduction Steps

  1. Start a conversation with several tool calls that produce large outputs (file reads, grep results, etc.)
  2. Continue the conversation until 20+ messages with tall content exist
  3. Try scrolling to the top — observe that only the most recent ~5 messages are reachable
  4. Send another long message/response — observe that the scrollable range shrinks further
  5. Alternative (more severe): Start a session, generate significant history, /exit, then claude --resume — the entire previous session's messages are unreachable immediately

Observed Behavior

  • Scrolling up stops after ~5 recent messages, even though the conversation has dozens
  • The Logo/welcome banner is visible at the scroll limit, but all messages between the logo and recent ones are missing
  • The reachable history progressively shrinks as the conversation grows — content that was scrollable moments ago becomes unreachable after a new tall response arrives
  • g key (jump to top) also cannot reach older messages — confirms the scroll range itself is truncated, not just mouse/wheel input
  • The issue is worse on resume — a resumed conversation with 80+ messages may only show the last 3-5

Suspected Mechanism

The TUI uses virtual scrolling that only mounts messages within a fixed row budget around the viewport. Messages outside the mounted range are replaced with spacer elements whose heights are estimated, not measured.

The behavior is consistent with the height estimates being far too low for unmeasured messages. Tool call results, file reads, and code blocks can easily be 50-200 terminal rows, but the spacers seem to assume only a few rows each. This makes the scroll container think there's very little content above the viewport, capping maxScroll too low.

This creates a chicken-and-egg problem: you can't scroll up to older messages because the scroll range is too small, and the heights can't be corrected until the messages are scrolled into view and rendered.

On resume, the problem is acute because all prior messages are unmeasured simultaneously, so the entire history's height is severely underestimated.

Environment

  • Claude Code version: v2.1.89
  • Tested terminals: macOS Terminal.app, Ghostty — issue reproduces in both
  • Platform: macOS (Darwin 25.3.0)

Expected Behavior

All messages in the conversation should be reachable via scrolling, regardless of session length or whether the session was resumed.

Possible Mitigation Directions

  • Use a higher default height estimate for unmeasured messages (the current estimate seems optimized for short single-line messages, not tool outputs)
  • Use a context-dependent estimate (e.g., higher for resumed/bulk-loaded messages where most content is tool results)
  • On scroll-up, speculatively mount a sampling of unmeasured messages to discover more accurate heights
  • Persist measured heights to the session file so resumed conversations start with accurate estimates

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions