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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions