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
onChatRecovery is never invoked when turn is ended by stream-stall watchdog, leaving SSE stream open indefinitely #2042
onChatRecovery is never invoked when turn is ended by stream-stall watchdog, leaving SSE stream open indefinitely
Description
Describe the bug
When a chat turn is terminated by the stream-stall watchdog, the recovery engine opens an incident, schedules _chatRecoveryContinue, and emits chat:recovery:skipped without ever calling the agent's onChatRecovery hook.
Additionally, the server-side chat() promise never settles, so neither onDone nor onError fires, leaving the client's SSE response (text/event-stream) open indefinitely after receiving the initial start frame (measured up to 180s before timing out).
Note: onChatRecovery works as expected on Durable Object eviction / deploy interruptions.
To Reproduce
Steps to reproduce the behavior:
Configure a low stall threshold in beforeTurn:
return{chatStreamStallTimeoutMs: 300};
POST a normal chat turn and read the SSE body using a bounded reader.
Observe that only 1 frame (data: {"type":"start",...}) is emitted.
The stream remains open indefinitely, onChatRecovery is never invoked, and wrangler tail logs show chat:recovery:skipped with attempt: 1, maxAttempts: 10, recoveryKind: "continue".
Expected behavior
Per the documented lifecycle in ChatRecoveryEngine.handleChatFiberRecovery (step 3 & 5):
An interrupted turn with remaining budget should invoke onChatRecovery.
Active streams should be closed/completed via completeRecoveredStream(streamId) to prevent client hangs.
Screenshots
N/A (See Wrangler tail JSON trace in additional context).
Version:
agents: 0.20.1 (also reproduced on 0.16.2)
@cloudflare/think: 0.15.1 (also reproduced on 0.9.1)
Source Trace:
In agents/dist/chat/index.js, invokeOnChatRecovery is only reachable inside handleChatFiberRecovery. The stall path arrives via the scheduled _chatRecoveryContinue callback and reaches chat:recovery:skipped without entering handleChatFiberRecovery.
Environment details:
Runtime: Cloudflare Workers
Compatibility Date: 2026-02-01
Flags: nodejs_compat
Architecture: Turn delegated to DO from an Astro API route; DO returns SSE stream.
Activity
fix(think): retry pre-first-chunk stalls and call onChatRecovery for …