The realtime stream that backsDocumentation Index
Fetch the complete documentation index at: https://trigger-docs-tri-7532-ai-sdk-chat-transport-and-chat-task-s.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
chat.agent enforces a per-record cap of ~1 MiB (1048576 bytes minus a small envelope reserve). Anything written through the chat output — auto-piped LLM chunks, chat.response.write, chat.store.set, custom writer.write parts — counts as one record per chunk and is rejected if it crosses the cap.
This is a platform-level limit and cannot be raised per project or per stream.
What you’ll see
When a chunk crosses the cap, the run fails with a typedChatChunkTooLargeError:
chunkType— discriminant on the chunk that failed (e.g.tool-output-available,data-handover,text-delta).chunkSize— UTF-8 byte count of the JSON-serialized record.maxSize— the effective cap.
Most common cause: large tool outputs
If you return astreamText result from run(), the AI SDK auto-pipes its UIMessageStream into the chat output. A tool whose result object is large (a fetched HTML body, a CSV blob, an image as base64, a deep DB row dump) gets emitted as one tool-output-available chunk — and that’s the chunk that overruns.
Diagnose first: log tool sizes during development.
Pattern 1: ID-reference (recommended)
Store the large value in your own database (or object store) and emit only an identifier through the chat stream. The frontend fetches the full payload separately on demand. This keeps the chat stream small, predictable, and resumable, and lets you reuse the value across turns or sessions without re-streaming it.chat.response.write — push the heavy value to your DB, then emit a small data part with the id:
Pattern 2: Out-of-band streams.writer()
If the value is only useful for the lifetime of the run (a long log tail, a transient progress dump, a per-turn debug trace) and you don’t want to persist it, write it to a separate run-scoped stream instead. Run-scoped streams.writer() is its own channel — chunks go through the same per-record cap, but the chat stream stays untouched, and useRealtimeRunWithStreams consumes them independently of the chat UI.
What does not trigger the cap
These calls don’t go through the realtime stream and have no per-record cap:chat.history.set/slice/replace/remove— locals-only mutations on the in-memory message list.chat.inject— appends to the run’s pending message queue, not the stream.chat.defer— promise registry; awaited at turn boundaries, never serialized to the stream.
chat.agent emits internally (trigger:turn-complete, trigger:upgrade-required) are tiny by construction.
See also
- Error handling — how
ChatChunkTooLargeErrorflows through the layers. - Database persistence — your own store as the durable backing for ID references.
- Client protocol — chunk shapes that travel on the chat stream.

