Skip to content

Opening an existing (cached) thread does not land at its newest message #6564

Description

@M3gA-Mind

Summary

Reported: opening an existing thread does not land at its newest message. Confirmed to reproduce in a running dev app.

Filing this separately and explicitly UNFIXED, because an attempted fix turned out to be a no-op and the defect does not reproduce in the e2e harness. Recording what was tried so the next attempt does not repeat it.

The suspected cause

useOpenThreadAtBottom (thread.tsx) is a useLayoutEffect that reads viewport.scrollHeight and scrolls in the same layout pass as the transcript's first render — before markdown, code blocks, tool timelines or async content have gained height. One shot, no retry, no rAF, and the latch is burned before the scroll lands.

The cached path is the one that matters. useOpenHumanExternalStore reads state.thread.messagesByThreadId, a cache cleared only on delete or sign-out:

  • a thread opened for the first time briefly renders empty, which re-arms assistant-ui's scrollToBottomOnInitialize, so it scrolls correctly even unfixed;
  • a thread visited earlier in the session hands its messages over on the very render its id changes, never passes through the empty state, and keeps the previous thread's scrollTop.

So any fix must be checked with open A → open B → return to A. A fresh-thread check passes either way and proves nothing.

What was tried, and why it was withdrawn

Re-arming the new bottom-follower (#6563) inside useOpenThreadAtBottom, so that later height changes re-pin the bottom and turn the one-shot scroll into one that survives content arriving.

It is a no-op. Measured against an A → B → A Playwright case that additionally scrolls thread B away from the bottom first, so the follower's flag is false on return and only an explicit re-arm could rescue the landing:

tree cached-path test
re-arm present passes
re-arm removed, follower kept passes
whole thread.tsx reverted to main passes

Passing in all three states means the test cannot detect this defect, so it was never evidence. The change was removed rather than shipped behind a comment claiming it mattered.

Why it does not reproduce in the harness

The e2e fixture's reply is plain markdown, whose full height is available in the first layout pass. The one-shot scroll therefore lands correctly and there is nothing to catch.

A fixture that reproduces this needs content whose height arrives after first paint — images that decode asynchronously, or a transcript whose late layout differs from its first, or a thread large enough that the transcript renders progressively. Until such a fixture exists, this is only reproducible by hand.

Repro by hand

  1. Open a thread with enough content to overflow, let it settle.
  2. Open a different thread.
  3. Return to the first thread.
  4. It should be at its newest message; it is not.

The regression guard added in #6563's PR (returning to an already-cached thread lands at the bottom) covers the behaviour main already has via #6475, and is labelled in-file as not being evidence for any fix.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions