Describe the bug
When an AIChatAgent with chatRecovery = true recovers an interrupted chat stream, the recovered server-side continuation can complete under a new request id while the browser-side useAgentChat / AI SDK useChat state remains stuck streaming.
The UI can keep reporting chat.status === "streaming" and chat.isStreaming === true even after recovery has finished.
To Reproduce
Steps to reproduce the behavior:
-
Clone/run this minimal repro: https://github.com/ThomasKliszowski/cloudflare-ai-chat-recovery-stuck-stream-repro.
-
Install dependencies:
-
Start the Worker:
-
In another terminal, start the client:
-
Open http://localhost:5173.
-
Click Start slow stream.
-
Wait until the page shows Ready for restart.
-
Stop the Worker with Ctrl-C.
-
Restart the Worker:
-
Wait for the browser to reconnect and for the server-side recovery continuation to finish.
-
Observe that the page can show Bug reproduced: the recovery continuation was received, but chat.status / isStreaming still indicate that the original browser stream is active.
Expected behavior
After the recovered continuation completes, the browser-side chat state should return to idle:
chat.status should no longer be "streaming" or "submitted".
chat.isStreaming should become false.
- The original browser request should no longer be considered active.
Version
Observed with:
agents@0.12.3
@cloudflare/ai-chat@0.6.2
ai@6.0.168
@ai-sdk/react@3.0.170
Additional context
The suspected cause is that WebSocketChatTransport.sendMessages() creates a local ReadableStream for the original request id. When chatRecovery takes over, the server sends continuation frames with continuation: true under a new request id.
Those continuation frames complete successfully, but they do not close or resolve the original local ReadableStream. As a result, AI SDK useChat can keep the original request in streaming state indefinitely.
Describe the bug
When an
AIChatAgentwithchatRecovery = truerecovers an interrupted chat stream, the recovered server-side continuation can complete under a new request id while the browser-sideuseAgentChat/ AI SDKuseChatstate remains stuck streaming.The UI can keep reporting
chat.status === "streaming"andchat.isStreaming === trueeven after recovery has finished.To Reproduce
Steps to reproduce the behavior:
Clone/run this minimal repro: https://github.com/ThomasKliszowski/cloudflare-ai-chat-recovery-stuck-stream-repro.
Install dependencies:
Start the Worker:
In another terminal, start the client:
Open
http://localhost:5173.Click Start slow stream.
Wait until the page shows Ready for restart.
Stop the Worker with
Ctrl-C.Restart the Worker:
Wait for the browser to reconnect and for the server-side recovery continuation to finish.
Observe that the page can show Bug reproduced: the recovery continuation was received, but
chat.status/isStreamingstill indicate that the original browser stream is active.Expected behavior
After the recovered continuation completes, the browser-side chat state should return to idle:
chat.statusshould no longer be"streaming"or"submitted".chat.isStreamingshould becomefalse.Version
Observed with:
agents@0.12.3@cloudflare/ai-chat@0.6.2ai@6.0.168@ai-sdk/react@3.0.170Additional context
The suspected cause is that
WebSocketChatTransport.sendMessages()creates a localReadableStreamfor the original request id. WhenchatRecoverytakes over, the server sends continuation frames withcontinuation: trueunder a new request id.Those continuation frames complete successfully, but they do not close or resolve the original local
ReadableStream. As a result, AI SDKuseChatcan keep the original request instreamingstate indefinitely.