Skip to content

useAgentChat: "Maximum update depth exceeded" thrown during stream resume after a reconnect — surfaces as a terminal error mid-turn (not fixed by #1838) #1913

Description

@mrgarcia1998

Describe the bug

When a chat client's WebSocket closes mid-turn and PartySocket auto-reconnects, the resumed stream's replayed chunks drive a synchronous re-render cycle inside the shared chat hook (agents/chat/react, consumed via @cloudflare/think/react): onAgentMessage → ReactChatState set messages → forceStoreRerender → … repeating until React's update-depth guard throws Maximum update depth exceeded.

The exception escapes through useAgentChat's catch path, so the client lands in status: "error" with error set mid-turn — while the durable turn on the agent DO is healthy and completes normally (verified in the DO message store: stream metadata completed, full assistant reply persisted).

#1838 does not fix this: with resumes serialized, a single close + its one resumed stream is still enough to trigger the loop. Reproduced in 3 separate runs on the versions below.

To Reproduce

  1. A Think-based agent with chat over useAgent + useAgentChat (defaults — resume on).
  2. Start a long streaming turn (a multi-second reply is enough).
  3. Close the underlying WebSocket once mid-stream (e.g. grab the raw socket and call .close(); PartySocket auto-reconnects).
  4. On reconnect the stream resumes and replays; the browser console shows the Maximum update depth exceeded React error, and useAgentChat reports status: "error" + a set error while the server-side turn keeps running.

Expected behavior

A resumed stream replays without tripping React's update-depth guard — and in general, a client-side rendering failure should not mark a turn as terminally errored while it is still running (and later completes) server-side. The false terminal error also cascades into a second bug (filed separately, linked below): once the client sits in "error", stream resume never re-arms, so isServerStreaming can stick true forever.

Version:

agents@0.17.3, @cloudflare/think@0.12.1, ai@6.0.205, @ai-sdk/react@3.0.207, react@19.2.7, partysocket@1.3.0. Observed both under local wrangler dev and in production.

Additional context

Found while adopting the #1799 connectionError surface. Downstream we mitigate with an error-triggered debounce + a bounded getMessages reconciliation loop, but the root loop lives in the shared hook's message-replay path. Happy to provide the full captured stack / more repro detail if useful.

Metadata

Metadata

Assignees

No one assigned

    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