Web chat pinned on "Loading history…" forever when a live assistant baseline fails validation #6954
Replies: 1 comment
|
Your chain is confirmed against HEAD ( The escape path is exactly } catch (error) {
if (generation !== this.openGeneration || this.events !== events) return
if (!isRemoteFailure(error)) throw error
this.events = undefined
this.openState = 'error'with One naming drift: HEAD has no Your "storage looks clean" asymmetry is also source-supported: durable entries are wire-validated ( On the fixes: fix 2 (settle Not verified: your 13,502-stream validation sweep and the 30s/wild observations are your measurements. If you capture the exact frame shape that triggered it in the wild, it'd be useful for anyone proposing a fix. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
session.followincludes the host's in-memory copy of an in-flight assistantattempt in its opening snapshot. The host folds those frames as trusted and never
validates chunk shape. The browser validates them strictly during the first
window install. When a chunk fails,
expandAssistantStreamthrows a plainTypeError, and every handler on the open path recognises onlyRemoteError.The
TypeErroris rethrown beforeopenStateis ever assigned, so the chatstays on
"Loading history…"with no error surface, no timeout and no retry.Users see a chat that never loads. Reloading does not help, because the same
baseline is served again. It clears only when the host drops the attempt.
Present in
0.1.5-rc.1and unchanged in0.1.6-alpha.1.Reproduction
Against a running
dsh web, intercept the gateway socket and attach anactiveAttemptwhose stream carries one non-object chunk to the openingsnapshot frame:
Result: the chat column shows
chat.loadingHistoryindefinitely, zero rows, noopenError, and the console carries an unhandledTypeError: Assistant stream raw chunk must be a lossless JSON object.Observed for 30 s, and in the wild for minutes across repeated hard refreshes.
Chain
All paths in
@deepseek-ai/dsh-api-session-controller.lib/index.js—SessionAssistantStreamAccumulatorretains live frames forreconnecting web followers, documented as folding "one trusted frame". No
chunk validation.
lib/index.js—follow()emitsassistantStream: this.assistantStreams.get(target)?.snapshot() ?? { revision: 0 }in the opening snapshot.
lib/client.js—ClientAssistantStream.replace()callsexpandAssistantStream(opening.stream), which runsvalidateRecordandthrows
TypeErrorfor a chunk that is not a lossless plain JSON object.That call sits under
installWindow→acceptEventChange('replace')→RemoteJournalStream.replaceFromOpening→publish, insideopen()'s tryblock, so it rejects
events.open().lib/client.js—doOpen:isRemoteFailurerequiresisDSHRemoteError === true, so aTypeErrorescapes.
openStateis never moved off"loading".followCurrent()callsrecord.session.open()with no.catch(), so therejection is unhandled and nothing re-opens:
followCurrentreturns earlywhile
current === this.watched.failEventStreamhas the same shape, so a non-RemoteErrorraised by thebackground consumer also throws out of a callback rather than settling state.
Why storage looks clean
Only the live copy is affected. Validating every stored assistant stream with
the shipped browser validator gave zero rejections over 13,502 streams in 567
sessions. A malformed durable event behaves correctly and renders
chat.loadError. The asymmetry is that durable events cross a validatingboundary and the live baseline does not.
Suggested fixes
so a bad chunk cannot reach the wire and fails where it can be attributed.
doOpenandfailEventStreamshouldsettle
openStatefor every error class, not onlyRemoteError; rethrowinginside a callback leaves the UI with no way back.
ClientAssistantStream.replace(): drop the transient tail, keep the durablewindow, let live frames resync. The in-flight text arrives again anyway.
Fix 2 alone converts a dead screen into a visible error. Fix 3 keeps the session
usable. We are running 3 locally as a patch.
Environment
@deepseek-ai/dsh0.1.5-rc.1,@deepseek-ai/dsh-api-session-controller0.1.5-rc.2@deepseek-ai/dsh-api-session-controller@0.1.6-alpha.1: thedoOpenandfailEventStreamhandling is byte-identical.All reactions