TanStack AI puts the finish reason and the model id in AG-UI metadata. useChat reads those fields from there.
Put these extras in metadata.tanstack on the event. The spec fields stay at the top level.
Note
A TanStack chat() server already writes metadata.tanstack. If your server sends AG-UI events itself, use this page.
Copy this event. Change the ids, the counts, and the model:
{
"type": "RUN_FINISHED",
"threadId": "thread-1",
"runId": "run-1",
"usage": [
{
"inputTokens": 12,
"outputTokens": 34,
"totalTokens": 46
}
],
"metadata": {
"tanstack": {
"finishReason": "stop",
"model": "gpt-6.1-sol"
}
}
}Check chunk.type, then read chunk.metadata?.tanstack. You do not need a helper:
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
export function Chat() {
const { messages } = useChat({
connection: fetchServerSentEvents("/api/chat"),
threadId: "support-chat",
onChunk: (chunk) => {
if (chunk.type === "RUN_FINISHED") {
console.log(chunk.usage);
console.log(chunk.metadata?.tanstack?.finishReason);
console.log(chunk.metadata?.tanstack?.model);
}
},
});
return <pre>{JSON.stringify(messages, null, 2)}</pre>;
}Send a message. The console now shows the token usage, "stop", and the model id.
The client copies metadata.tanstack from the wire event onto the chunk. Spec fields such as usage stay where the spec puts them.
Send these fields on every successful run:
| Field | Where | Why the client needs it |
|---|---|---|
| type, threadId, runId | Spec top level | They frame the run. Without the ids, resume and correlation break. |
| usage[] | Spec top level on RUN_FINISHED and RUN_ERROR | Token counts: inputTokens, outputTokens, totalTokens. |
| finishReason | metadata.tanstack on RUN_FINISHED | After a tool result, the client continues only when this is not "stop". |
| model | metadata.tanstack | Generation reconstruct uses it. |
finishReason is one of these values: "stop", "length", "content_filter", "tool_calls", or null.
Warning
Always send finishReason after a tool result. If it is missing, the client treats it as not "stop" and can send another turn.
For the full event list, see Stream Events. For the request body, see AG-UI Client Compliance.
The wire uses the spec usage[] array with inputTokens. In process, usage is the TanStack TokenUsage type with promptTokens. The client rebuilds TokenUsage from the array.
An AG-UI RUN_ERROR has message and an optional code at the top level. Put the correlation ids in metadata.tanstack:
{
"type": "RUN_ERROR",
"message": "Provider timeout",
"code": "TIMEOUT",
"metadata": {
"tanstack": {
"threadId": "thread-1",
"runId": "run-1",
"model": "gpt-6.1-sol"
}
}
}For a rate limit, put the wait time in metadata.tanstack.retryAfterMs, in milliseconds. Clients read it from there. See Rate limits.
Put the provider error type in metadata.tanstack.errorType and the request id in metadata.tanstack.requestId. See Read the provider error type and request id.
When you use one of these features, add its field: