Skip to content

ai-chat: chat-recovery exhaustion has no terminal-status hydration (disconnected client misses the terminal banner on reconnect) #1645

Closed

Description

@threepointone

Summary

In @cloudflare/ai-chat, when chat recovery is exhausted, _exhaustChatRecovery delivers the terminal UX only as a transient broadcast (_broadcastChatMessage with done: true, error: true). It does not durably record the terminal status. A client that is disconnected at the moment recovery gives up (e.g. mid deploy-churn / WS reconnect storm — the exact condition under which recovery exhausts) will never see the terminal banner on reconnect, and the turn appears frozen.

@cloudflare/think does not have this gap: its _exhaustChatRecovery additionally calls _recordTerminalChatStatus("interrupted", …), which persists CHAT_LAST_TERMINAL_KEY, and _buildIdleConnectMessages replays that terminal error to a client on (re)connect. ai-chat has no equivalent.

Scope

This affects all ai-chat recovery exhaustion paths (deploy/eviction exhaustion via _handleInternalFiberRecovery, and the stable-state-timeout give-up), not any single path. It is pre-existing.

Where

  • packages/ai-chat/src/index.ts — _exhaustChatRecovery only broadcasts; no durable terminal record.
  • Compare packages/think/src/think.ts — _exhaustChatRecovery → _recordTerminalChatStatus("interrupted", …), plus _buildIdleConnectMessages / _pendingTerminalChatResponse for replay on idle connect.

Repro sketch

  1. Start a turn on an ai-chat agent with chatRecovery enabled and a custom terminalMessage.
  2. Drive recovery to exhaustion while the client socket is closed (e.g. simulate deploy churn so the budget drains while disconnected).
  3. Reconnect the client.
  4. Expected: the client receives a terminal error message (the configured terminalMessage) on connect.
  5. Actual: no terminal frame is replayed; the UI shows no resolution for the abandoned turn.

Proposed fix

Give ai-chat a durable last-terminal record analogous to think's: persist the terminal message/requestId in _exhaustChatRecovery and replay it in the agent's on-connect message build, so an exhausted turn surfaces on reconnect rather than freezing.

Context

Spun out of the chat-recovery stable-state-timeout exhaustion fix (which routes the give-up through onExhausted/terminalMessage like deploy/stall exhaustion). During that review we noted ai-chat's weaker terminal hydration is a separate, pre-existing concern and out of scope for that change.

Activity

  1. self-assigned this
    on Jun 2, 2026
  2. added a commit that references this issue on Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions