Skip to content

useAgentChat still hits React #185 with a 50ms throttle on agents@0.22.0 #2267

Open

Description

@banchichen

Summary

useAgentChat can still enter status: "error" with React #185 (Maximum update depth exceeded) during a long streamed turn, even when chat updates are explicitly throttled at 50 ms.

This looks related to #1361 / #1732, but the occurrence described below is on agents@0.22.0, after the default-throttle work in #2058, with experimental_throttle: 50 explicitly configured. The application also carries an equivalent of the proposed #2217 fix, so the clientToolResults no-op cleanup dispatch should not be the remaining source.

Environment

  • agents@0.22.0
  • ai@7.0.93
  • @ai-sdk/react@4.0.96
  • react@19.2.7
  • Mobile Safari
  • Production build
  • useAgentChat({ resume: true, experimental_throttle: 50 })

Actual behavior

During a long streamed chat turn, the client intermittently throws React #185. The source-mapped stack reaches the Agents chat response handling / setMessages path, then the AI SDK ReactChatState subscriber callback and React's forceStoreRerender. The client stream is assigned an error even though this is a React-side update-depth failure.

Why this appears distinct from #2217

The affected application has a pre-dispatch stale-entry check equivalent to the fix proposed in #2217. The clientToolResults cleanup effect does not call setClientToolResults unless a stale entry actually exists, so it no longer schedules a no-op update on every streamed chatMessages change.

Suspected remaining update source

The fallback/observer branch still applies each live broadcast update directly to the AI SDK store:

const result = broadcastTransition(streamStateRef.current, {
  type: "response",
  streamId: data.id,
  // ...
});
streamStateRef.current = result.state;

if (result.messagesUpdate) {
  setMessages(result.messagesUpdate);
}

Current main still has this write:
https://github.com/cloudflare/agents/blob/main/packages/agents/src/chat/react.tsx#L2157-L2163

For a live observed stream, broadcastTransition can produce a messagesUpdate for every chunk. The configured useChat throttle limits React publication frequency, but it does not prevent these per-chunk setMessages calls from entering the store. This is a candidate rather than a proven root cause.

One possible mitigation would be to coalesce the latest accumulated observer update before calling setMessages, with an immediate flush for done / replayComplete and cancellation on clear/unmount.

Reproduction status

We do not yet have a deterministic standalone reproduction. Stress attempts in Chromium with a mobile viewport did not reproduce it; the failure has only been observed intermittently in Mobile Safari so far. We can provide a sanitized source-mapped stack if useful.

Expected behavior

Long and/or observed streamed turns should complete without exhausting React's nested-update budget or moving useAgentChat to status: "error".

Related

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions