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
Stream resumption loses reasoning/thinking state after page refresh #896
When a page is refreshed during an active stream that includes reasoning/thinking (e.g., OpenAI o-series or GPT models with thinking enabled), the "thinking" state is lost after reconnect. The text response continues correctly via stream resumption, but reasoning parts are no longer visible.
Root cause
_sendStreamChunks replays all stored chunks synchronously in a tight loop:
On the client, each chunk triggers a setMessages call. React batches all these state updates and only renders the final state. By that time, reasoning-end has already been processed, so reasoning parts have state: 'done' and are never visible to the user — even though the model may still be actively thinking.
During the original stream this isn't an issue because chunks arrive gradually (as the LLM generates tokens), giving React time to render intermediate states like "Thinking...".
Secondary issue
There's also a race condition between onConnect (which sends CF_AGENT_STREAM_RESUMING) and the useEffect in useAgentChat that registers the message handler. If the WebSocket connects before the handler is registered, the notification is lost and stream resumption doesn't happen at all.
Suggested fix directions
Expose a "replaying" flag so the client knows chunks are being replayed and can preserve intermediate states
Send chunks with microtask yields between them so React can render intermediate states
For the race condition: have the client request stream resumption after the handler is ready, rather than relying solely on the server pushing it in onConnect
This isn't fully fixed on @cloudflare/ai-chat@0.1.4. The race condition fix works, but active streams still lose state after reconnect.
After replayChunks() sends all stored chunks with replay: true, there's no flush to React state for active streams. flushActiveStreamToMessages only runs when data.done is true during replay, but for active streams done is never sent after replay. The replayed chunks sit in activeStreamRef unflushed until the next live chunk arrives.
Result: After refreshing during a thinking phase, messages contains only the user message (no assistant message), status stays ready, and the UI shows nothing until the model finishes thinking and the first text token arrives as a live chunk.
The server should signal replay completion for active streams so the client can flush the accumulated state.
@threepointone Tested npm i https://pkg.pr.new/cloudflare/agents/@cloudflare/ai-chat@989 — the replayComplete flush works (assistant message now appears after refresh), but status from useChat stays "ready" during stream resumption.
The resumed chunks flow through the raw onAgentMessage WebSocket handler, which calls flushActiveStreamToMessages → setMessages. But the AI SDK's useChat transport is not involved, so its status never transitions to "streaming".
In our case this means after refresh during an active stream:
Messages show correctly (user + partial assistant) ✅
status stays "ready" instead of "streaming" ❌
isLoading is false → no abort button, no thinking indicator
UI appears idle even though the model is still generating
The transport's reconnectToStream returns null, so useChat has no way to know about the resumed stream. Possible fixes:
Have reconnectToStream return a ReadableStream that receives the replayed + live chunks
Expose a setStatus override so onAgentMessage can force "streaming" during replay
Tested PR #999 — status still stays "ready" after page refresh during an active stream.
Root cause confirmed with debug logging: reconnectToStream is called while the PartySocket readyState is 3 (CLOSED). The onDetect listener is registered on the old (dead) internal WebSocket. By the time PartySocket reconnects and the server responds, the 3s timeout has already cleaned up the listener.
Activity
threepointone commentedon Feb 12, 2026
I'll fix this, thanks for the report
whoiskatrin commentedon Feb 13, 2026
@alexanderjacobsen now this is the issue that should be fixed in the attached PR
threepointone commentedon Feb 14, 2026
This is fixed in main, should go out this coming week.
alexanderjacobsen commentedon Feb 25, 2026
This isn't fully fixed on
@cloudflare/ai-chat@0.1.4. The race condition fix works, but active streams still lose state after reconnect.After
replayChunks()sends all stored chunks withreplay: true, there's no flush to React state for active streams.flushActiveStreamToMessagesonly runs whendata.doneis true during replay, but for active streamsdoneis never sent after replay. The replayed chunks sit inactiveStreamRefunflushed until the next live chunk arrives.Result: After refreshing during a thinking phase,
messagescontains only the user message (no assistant message),statusstaysready, and the UI shows nothing until the model finishes thinking and the first text token arrives as a live chunk.The server should signal replay completion for active streams so the client can flush the accumulated state.
cc @threepointone
threepointone commentedon Feb 25, 2026
fixing
fix: flush replayed stream state and handle orphaned streams after hi…
fix: flush replayed stream state and handle orphaned streams after hi…
threepointone commentedon Feb 25, 2026
try this and tell me if it works for you?
npm i https://pkg.pr.new/cloudflare/agents/@cloudflare/ai-chat@989fix: flush replayed stream state and handle orphaned streams after hi…
alexanderjacobsen commentedon Feb 26, 2026
@threepointone Tested
npm i https://pkg.pr.new/cloudflare/agents/@cloudflare/ai-chat@989— thereplayCompleteflush works (assistant message now appears after refresh), butstatusfromuseChatstays"ready"during stream resumption.The resumed chunks flow through the raw
onAgentMessageWebSocket handler, which callsflushActiveStreamToMessages→setMessages. But the AI SDK'suseChattransport is not involved, so itsstatusnever transitions to"streaming".In our case this means after refresh during an active stream:
statusstays"ready"instead of"streaming"❌isLoadingis false → no abort button, no thinking indicatorThe transport's
reconnectToStreamreturnsnull, souseChathas no way to know about the resumed stream. Possible fixes:reconnectToStreamreturn aReadableStreamthat receives the replayed + live chunkssetStatusoverride soonAgentMessagecan force"streaming"during replaythreepointone commentedon Feb 26, 2026
I'll have a look
threepointone commentedon Feb 26, 2026
Here's another go:
npm i https://pkg.pr.new/cloudflare/agents/@cloudflare/ai-chat@999alexanderjacobsen commentedon Feb 26, 2026
Tested PR #999 —
statusstill stays"ready"after page refresh during an active stream.Root cause confirmed with debug logging:
reconnectToStreamis called while the PartySocketreadyStateis3(CLOSED). TheonDetectlistener is registered on the old (dead) internal WebSocket. By the time PartySocket reconnects and the server responds, the 3s timeout has already cleaned up the listener.Reopened #1000 with details and console output.
fix: flush replayed stream state and handle orphaned streams after hi…
threepointone commentedon Feb 27, 2026
Closing this issue for #1000