[BUG] claude.ai/code (web): transcript jumps to much older messages when the message list is re-keyed (e.g. a streamed reply is finalized) — virtualizer loses its anchor (`rec-lost`) and keeps the raw scrollTop

Status Open
Maintainer reply None cached
Activity 0 comments · opened Oct 2, 2026

Environment

  • Claude Code on the web (claude.ai/code), Google Chrome 154.0.8037.93, macOS 26.6.2 (arm64)
  • Long sessions with many tool calls; observed 3 times on 2026-10-02 (two traces below)

What happens
While reading the latest reply, the transcript suddenly shows a much older part of the conversation. The user has to drag back down to find where they were. The jump is not caused by user input: it happens when the message list itself is updated (a reply finishes streaming, or the list is re-fetched).

Expected
The row being read stays where it is when the list changes around it (items prepended above, items re-keyed when a streamed message gets its final id).

Evidence (the transcript's own debug trace, window.__rsTrace = [])

Case A — reply finalized (temporary id -> final id) + rows inserted above, while the user reads (slow downward scroll):

  pick aIdx=8 key="<uuid-T>›<uuid-T>:card:<toolu_X>" st=576
  range start=0 end=2 ext=4274 st=576   (rows inserted above, scrollTop unchanged)
  pick aIdx=0 key="msg_B" st=576       (anchor lost -> row 0, a long older message)
  meas idx=14 key="msg_C›msg_C:card:<toolu_X>" prev=null   (same card, new key, index 8 -> 14)

Case B — whole window replaced by earlier turns, no user input (~1.4 s after a small upward nudge that was compensated correctly):

  comp delta=368 write before=430 got=798
  pick aIdx=11 key="msg_D›msg_D:t1114113" st=795
  range end=16 ext=1748 -> 2929, layout itemsChanged=true, meas idx=0..15 prev=null
  rec-lost key="msg_D›msg_D:t1114113"
  pick aIdx=1 key="msg_E" st=755   (same scrollTop, older content)

Minimal reproduction with the production virtualizer
Loaded the transcript virtualizer hook from the claude.ai client bundle (2026-10-01 build) into a plain page with the same DOM structure, 150 rows, estimate 120 px. A single setItems that re-keys every row and prepends 6 rows reproduces the jump every time: the row under the reading line changes for 25–66 frames (~2 s) (3 runs, frames counted in a ResizeObserver callback).

Suggested direction

  • When a streamed message is finalized, carry measured size and anchor from the temporary key to the final key (the block suffix, e.g. :card:<toolu_X>, is stable).
  • When the anchored key disappears (rec-lost), fall back to the nearest surviving row or the distance from the end of the list rather than keeping the raw scrollTop.

Related (closed): #68977, #83167.

View original on GitHub ↗