You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Platform defect: one withdrawing conversation definition kills the session's event feed, and no history page can load past it
#8822
Loading older history into the client's session window can fail permanently at a specific point in
a session log. The failure is not in the paging request: it is a throw inside the conversation node
assembler while the page is being published, and that throw takes the event feed subscriber with
it. From that moment every loadOlder() and loadThrough() resolves without delivering anything,
so history appears to be stuck at a fixed turn even though hasMore remains true.
The error
[session-controller] event feed subscriber failed:
Error: conversation Definition "system-message" withdrew materialized target "chat";
return the same key with hidden visibility instead
at ConversationNodeAssembler.buildTargetUpserts (client.js:2505)
at ConversationNodeAssembler.flush (client.js:2071)
at BoundConversation.flush (client.js:3017)
at BoundConversation.publish (client.js:3010)
at BoundConversation.accept (client.js:2979)
at sc (index-*.js:56)
at MutableSessionEventSource.publish (client.js:1208)
at MutableSessionEventSource.prepend (client.js:1170)
at Session.prependWindow (client.js:2144)
The same stack appears whether the caller is loadOlder (50-message pages, HISTORY_PAGE_OPTIONS)
or loadThrough (200-message pages, JUMP_PAGE_OPTIONS):
at Session.prependWindow
at loadThrough (client.js:1926)
Why the page never lands
paginate(events, beforeSeq, maxMessages, throughSeq, turnWindow) walks BACKWARDS from beforeSeq (which the client passes as the window's oldest loaded sequence) and returns a
contiguous batch ending there. A prepend therefore always spans [target, windowBase].
Consequence, and it is what makes this a hard block rather than an inconvenience: if the event that
withdraws the chat target sits anywhere between a requested target and the current window base, every possible request contains it, no matter how far back the target is. There is no page size
or target choice that avoids it. Measured on a real session, four targets spanning 778 sequences
(49787, 49697, 49467, 49009) all failed identically, with the rendered turn count unchanged at 34
and hasMore: true.
Why it looks intermittent
The block only applies while the offending event is still BEYOND (older than) the client's window.
Once the window has naturally grown past it — which is what happens when the failure occurred in an
earlier session view, or when the reader has already scrolled that far with a healthy feed — later
pages do not span it and load normally. That is why "jumping far back" sometimes works and then many
loads succeed.
Impact
The reading view (this plugin) cannot load older history past that point at all, and neither can
the host's own "load earlier" control, since both go through the same two loaders.
One bad event costs the session's entire event feed, not just the page it arrived in.
Suggested fix
The error message already names it: a definition that withdraws a materialized target should be
tolerated by returning the same key with hidden visibility instead of throwing. Beyond that,
treating a node-assembler failure as a failed PAGE (logged, skipped, feed preserved) rather than as
a failed SUBSCRIBER would keep the session usable: today the subscriber dies and every later load
becomes a silent no-op, which is indistinguishable from "there is no more history".
Reproduction
Open a session whose older history contains an event that withdraws the materialized chat
target (in the session that produced this report, it lies within roughly 800 sequences below the
initial window base).
Scroll up and load older history until the first prepend spans it.
Observe the console error above, then observe that hasMore stays true while no page lands —
including with the maximum page shape (loadThrough) and with targets hundreds of sequences
further back.
Evidence gathered from the client
loadOlder and loadThrough both produce the identical assembler throw.
After the first failure, subsequent requests from both loaders resolve without effect
(landed: false, unchanged rendered turn count, openState: 'open', hasMore: true).
The failure is per-session-view state, not per-request: a fresh view loads normally until its
window base is above the offending event again.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
Loading older history into the client's session window can fail permanently at a specific point in
a session log. The failure is not in the paging request: it is a throw inside the conversation node
assembler while the page is being published, and that throw takes the event feed subscriber with
it. From that moment every
loadOlder()andloadThrough()resolves without delivering anything,so history appears to be stuck at a fixed turn even though
hasMoreremainstrue.The error
The same stack appears whether the caller is
loadOlder(50-message pages,HISTORY_PAGE_OPTIONS)or
loadThrough(200-message pages,JUMP_PAGE_OPTIONS):Why the page never lands
paginate(events, beforeSeq, maxMessages, throughSeq, turnWindow)walks BACKWARDS frombeforeSeq(which the client passes as the window's oldest loaded sequence) and returns acontiguous batch ending there. A prepend therefore always spans
[target, windowBase].Consequence, and it is what makes this a hard block rather than an inconvenience: if the event that
withdraws the
chattarget sits anywhere between a requested target and the current window base,every possible request contains it, no matter how far back the target is. There is no page size
or target choice that avoids it. Measured on a real session, four targets spanning 778 sequences
(49787, 49697, 49467, 49009) all failed identically, with the rendered turn count unchanged at 34
and
hasMore: true.Why it looks intermittent
The block only applies while the offending event is still BEYOND (older than) the client's window.
Once the window has naturally grown past it — which is what happens when the failure occurred in an
earlier session view, or when the reader has already scrolled that far with a healthy feed — later
pages do not span it and load normally. That is why "jumping far back" sometimes works and then many
loads succeed.
Impact
the host's own "load earlier" control, since both go through the same two loaders.
Suggested fix
The error message already names it: a definition that withdraws a materialized target should be
tolerated by returning the same key with hidden visibility instead of throwing. Beyond that,
treating a node-assembler failure as a failed PAGE (logged, skipped, feed preserved) rather than as
a failed SUBSCRIBER would keep the session usable: today the subscriber dies and every later load
becomes a silent no-op, which is indistinguishable from "there is no more history".
Reproduction
chattarget (in the session that produced this report, it lies within roughly 800 sequences below the
initial window base).
hasMorestaystruewhile no page lands —including with the maximum page shape (
loadThrough) and with targets hundreds of sequencesfurther back.
Evidence gathered from the client
loadOlderandloadThroughboth produce the identical assembler throw.(
landed: false, unchanged rendered turn count,openState: 'open',hasMore: true).window base is above the offending event again.
All reactions