Skip to content

bug(assistant-ui): intermittent "Maximum update depth exceeded" during chart-heavy present_openui streaming renders #990

Description

@okisdev

Describe the bug

during long or chart-heavy present_openui streaming renders, React trips its infinite-update limiter. the console logs Maximum update depth exceeded (twice per occurrence), the thrown variant of the same error is caught by the integration's error boundary, so the ErrorFallback banner renders inside the assistant message and the tail of the streamed interface is truncated (in the run below, the last takeaway item's body is cut off mid-render).

it is intermittent but frequent: in the latest rerun on @openuidev/assistant-ui@0.0.3, 1 of 2 display-only runs crashed. on 0.0.1 it was 2 of 2 with the same model, and the failure there was harsher: the whole /api/chat fetch aborted with net::ERR_ABORTED mid-stream. on 0.0.3 the stream completes with HTTP 200 and only the render is damaged, so something between 0.0.1 and 0.0.3 already softened the blast radius without removing the loop.

versions: @openuidev/assistant-ui@0.0.3, @openuidev/react-headless@0.9.8, @openuidev/react-lang@0.2.11, @openuidev/react-ui@0.13.6, ai@7.0.62, @ai-sdk/react@4.0.65, @assistant-ui/react@0.15.14, react@19.2.8, next@16.3.0 (Turbopack dev), zustand@4.5.7.

To Reproduce

  1. clone https://github.com/assistant-ui/assistant-ui and run examples/with-openui (the readme has the steps; needs an OpenAI key)
  2. send a display-only prompt that produces a large interface with a chart, for example: "Summarize our sprint: 34 of 41 planned issues closed, velocity 62 points versus 55 last sprint, 3 escaped bugs, and a median cycle time of 2.4 days."
  3. watch the console during streaming; repeat once or twice if the first run is clean. the trip-summary suggestion sometimes renders clean, the sprint prompt crashed on the first try.

the example route pins gpt-5.6-luna; both the 0.0.1 and 0.0.3 crashes reproduced with it.

Expected behavior

the streamed interface renders to completion without tripping React's nested update limit, and no ErrorFallback banner appears for a stream that completed successfully.

Call stack, and where the update budget actually goes

full stack of the update that crosses the limit (Next dev overlay with ignore-listed frames expanded; innermost app-relevant frames):

forceStoreRerender                                    react-dom (via enqueueConcurrentRenderForLane / getRootForUpdatedFiber)
useChat updateMessages (subscribeToMessages)          @ai-sdk/react/src/use-chat.ts:158
subscriber notify (Set.forEach)                       @ai-sdk/react/src/chat.react.ts:102
ReactChatState.replaceMessage                         @ai-sdk/react/src/chat.react.ts:69
write                                                 ai/src/ui/chat.ts:783
processUIMessageStream                                ai/src/ui/process-ui-message-stream.ts:657
SerialJobExecutor.processQueue                        ai/src/util/serial-job-executor.ts:15

there are no OpenUI frames in that stack, but this does not look like an AI SDK bug: React reports the update that crosses the threshold, not the updates that consumed the budget. counterfactual: a stub toolkit with the same tool names and argument shapes but plain dumb renderers streams the same responses through the identical assistant-ui plus AI SDK pipeline with zero errors. so the nested-update budget appears to be consumed by render-time or effect-time state updates in the OpenUI renderer layer (OpenUIContent, the react-lang Renderer, the zustand store) reacting to each streamed ui argument update, and the AI SDK store rerender for the next chunk is merely the update that happens to cross the limit. reading the stack at face value would misattribute it.

Desktop

  • OS: macOS
  • Browser: Chrome (Next 16.3.0 dev server with Turbopack)

Additional context

this is the remaining item from the report in assistant-ui/assistant-ui#5925 (assistant-ui/assistant-ui#5925 (comment)). the other two items from that report are confirmed fixed on the current releases: the ai peer widening from #982 resolves cleanly under npm with AI SDK 7, and the follow-up actions fix from #985 now produces a proper natural-language continuation after a prompt_openui submit instead of raw params or empty text. happy to rerun with instrumentation or a patched build if that helps narrow it down.

Metadata

Metadata

Assignees

Labels

questionFurther information is requested

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions