Skip to content

bug(runtime-host): a transcript page that spans a Turn boundary can exceed the client's 256-message range cap #5183

Description

@Astro-Han

What happened

Scrolling into history in Maka Desktop fails the fill with

RangeError: Session transcript range exceeds the local capacity limit
    at decodeTranscriptPage (packages/runtime-host/src/client/session-subscription.ts:303)

The Main log shows it as a sessions:transcript:load-before failure. The Renderer's edge is then marked spent, so the reader gets no more rows in that direction until they move; scrolling back and forth retries and fails the same way every time. The rows exist and are well-formed — the read is refused by a size check, not by data.

Observed on a real workspace where two adjacent Turns hold 161 and 134 durable messages (295 together, each message small). Deterministic once the reader pages across that boundary.

Not a regression of #5170; main behaves the same. #4428 / #4433 fixed the same limit on the bootstrap path (an oversized target Turn now degrades to null boundaries). This is the page path, which #4433 did not touch.

Where the two limits meet

  • packages/runtime-host/src/protocol/session-transcript.ts:34-36SESSION_TRANSCRIPT_PAGE_MAX_MESSAGES = 256, and SESSION_TRANSCRIPT_RANGE_MAX_MESSAGES is the same number.
  • packages/runtime-host/src/server/session-transcript-pager.ts:296-419 — after selecting the page by bytes, the Host scans on from the page's far edge to the end of the Turn it landed in and returns that as rangeBoundarySequence, so the client will assemble the whole Turn rather than show a torn one. The budget check at :409-413 counts only the records of that extension (retainedMessages), not the rows already in the selected page.
  • packages/runtime-host/src/client/session-subscription.ts:292-304 — the client keeps pulling continuations until rangeBoundarySequence is in hand, counting every identity across the selected page and the continuations against SESSION_TRANSCRIPT_RANGE_MAX_MESSAGES, and throws when the total passes 256.

So the Host can promise a boundary the client is forbidden to reach: a 128 KiB page of small messages already holds ~200 rows, and the extension to the Turn's end adds the rest. Hypothesis, not yet verified with a unit test — the numbers in readSessionTranscriptPage need to be traced against a fixture of two adjacent Turns of ~160 and ~130 short messages.

How to reproduce

  1. A session with two adjacent Turns of roughly 160 and 130 short durable messages (a tool-heavy Turn does this naturally).
  2. Open the session at the tail; scroll up until the fill has to read across the boundary between them.
  3. The fill rejects with the RangeError above; the transcript stops short of the older Turn.

A pager unit test in packages/runtime-host/src/__tests__/session-transcript-pager.test.ts with such a fixture should reproduce it without the app: readSessionTranscriptPage for direction: 'older' with maxBytes: 128 * 1024 anchored inside the 134-row Turn, then assemble to rangeBoundarySequence the way decodeTranscriptPage does.

Expected

Either the Host never names a rangeBoundarySequence the client cannot legally reach (the extension budget must include the selected page, or degrade to a null boundary the way #4433 does for bootstrap), or the client's range cap counts the same thing the Host budgets. Whichever side moves, the two constants must describe one rule.

Changing the constant or the protocol validation at session-transcript.ts:260 is a compatibility-epoch change; a pager-side fix that keeps the wire shape is not.

Environment

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingeffort/MUnder 500 readable lineshelp wantedExtra attention is needed

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions