Skip to content

fix(react): backport throttled chat snapshots to v6 - #18533

Merged
gr2m merged 1 commit into
release-v6.0from
codex/backport-18525-v6
Aug 6, 2026
Merged

fix(react): backport throttled chat snapshots to v6#18533
gr2m merged 1 commit into
release-v6.0from
codex/backport-18525-v6

Conversation

@gr2m

@gr2m gr2m commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Background

useChat in AI SDK 6 throttles its messages subscription callback, but its useSyncExternalStore snapshot always reads the latest chat.messages array. Because streaming replaces that array for every chunk, an unrelated React render can observe a new snapshot before the throttled callback publishes it. In high-frequency streams this bypasses experimental_throttle, causes per-chunk renders, and can contribute to the "Maximum update depth exceeded" failures reported in #6166.

This is a branch-native backport of #18525 to release-v6.0.

Summary

  • Keep a published messages snapshot per useChat hook and advance it from that hook's throttled subscription callback.
  • Publish the latest message snapshot before ready or error becomes observable, including normal completion and aborts.
  • Synchronize updates that occur between render and subscription, and ignore delayed callbacks after a hook unsubscribes or changes chat instances.
  • Add regression coverage for unrelated renders, terminal status/message coherence, aborts, errors, and delayed callbacks after chat replacement.
  • Turn the existing Next.js throttle route into a deterministic end-to-end reproduction that streams 500 chunks while forcing unrelated renders, reports pass/fail from observed snapshot identities, and verifies the complete message is visible when status becomes ready.
  • Update the reproduction's stale text stream-part shape to the v6 protocol and avoid a render-counter hydration mismatch.
  • Add a patch changeset for @ai-sdk/react.

Contributor Credit

Manual Verification

Ran /chat/throttle in examples/ai-e2e-next in a real browser. The route streamed 500 chunks (1,000 assistant characters) with experimental_throttle: 50 while a zero-delay timer independently re-rendered the component.

The patched v6 run passed with 15 distinct message snapshots in 860ms (maximum expected: 22), across 462 total React renders. All 1,000 assistant characters were visible on the first render where status became ready, with no Next.js error overlay.

Also ran:

  • pnpm --filter @ai-sdk/react test -- use-chat.ui.test.tsx (60 tests passed)
  • pnpm check
  • pnpm type-check:full

Checklist

  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

Future Work

This backport intentionally leaves the v6 opt-in default unchanged, so applications must continue to set experimental_throttle to benefit from paced React publications.

For v8, I recommend making a 50ms UI publication cadence the default when throttle is omitted, with throttle: 0 as the explicit unthrottled opt-out. Stream processing, tool handling, and callbacks should remain immediate; only snapshots exposed to React should be paced. The default should guarantee an immediate leading publication and a terminal flush so final messages and ready status stay coherent.

This would cap the normal rendering rate at about 20 updates per second and protect applications that do not know they need to opt in today. The tradeoff is up to 50ms of additional visible text latency and an explicit opt-out for applications that intentionally need per-chunk rendering, which makes the behavior change appropriate for a major release.

Related Issues

Backport of #18525.

Addresses the throttled snapshot bypass discussed in #6166. Reports using the default unthrottled behavior remain outside this PR.

Related to cloudflare/agents#2058.

@gr2m
gr2m merged commit 987cab1 into release-v6.0 Aug 6, 2026
3 checks passed
@gr2m
gr2m deleted the codex/backport-18525-v6 branch August 6, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant