Summary
When using resumable streams with WorkflowChatTransport and a negative initialStartIndex (for example -50), the reconnect response can start in the middle of a logical UI-message part sequence.
That causes the AI SDK client stream processor to throw errors like:
Received reasoning-delta for missing reasoning part with ID "reasoning-0". Ensure a "reasoning-start" chunk is sent before any "reasoning-delta" chunks.
This happened even though the reconnect endpoint correctly returned x-workflow-stream-tail-index as described in the docs.
Versions
workflow: 4.2.4
@workflow/ai: 4.1.2
ai: 6.0.168
@ai-sdk/react: 3.0.170
What I expected
I expected negative resume indices to be safe to use for resumable chat streams, as long as:
- the client uses
initialStartIndex: -50
- the reconnect route returns
x-workflow-stream-tail-index
- the route returns
getRun(runId).getReadable({ startIndex })
What actually happened
The reconnect stream started with deltas for an already-open logical part, instead of the matching start chunk.
Example from Chrome SSE output:
data: {"type":"reasoning-delta","id":"reasoning-0","delta":" space in the header"}
data: {"type":"reasoning-delta","id":"reasoning-0","delta":".\n\nNow I"}
...
data: {"type":"reasoning-end","id":"reasoning-0"}
data: {"type":"tool-input-start","toolCallId":"call_exjjt7cc","toolName":"grep"}
...
data: {"type":"finish-step"}
data: {"type":"tool-output-available", ...}
data: {"type":"start-step"}
data: {"type":"reasoning-start","id":"reasoning-0"}
The AI SDK client then throws because it receives reasoning-delta before reasoning-start.
Why this seems surprising
The current docs around negative resume indices talk about transport/chunk offsets and x-workflow-stream-tail-index, but they do not mention that:
- a negative chunk index can land in the middle of a text-* sequence
- or in the middle of a reasoning-* sequence
- or in the middle of a tool-input-* sequence
The AI SDK UI stream processor enforces those semantic boundaries, so raw chunk-index resume is not always safe for UI chunk replay.
Minimal setup
Client:
new WorkflowChatTransport({
api: "/api/chat",
initialStartIndex: -50,
})
Reconnect route:
const run = getRun(params.id)
const stream = run.getReadable({
startIndex: startIndex ? parseInt(startIndex, 10) : undefined,
})
const tailIndex = await stream.getTailIndex()
return createUIMessageStreamResponse({
stream,
headers: {
"x-workflow-run-id": params.id,
"x-workflow-stream-tail-index": String(tailIndex),
},
})
Workaround I had to implement
On the server, when startIndex < 0, I had to:
- resolve the negative index against the tail index
- scan backward a bit
- rewind to the previous safe boundary such as start or start-step
- only then return the stream
That fixed the issue.
Request
Can the docs and/or library behavior be clarified here?
Possible directions:
- document that negative resume indices can cut into the middle of UI part sequences
- recommend rewinding to a safe boundary before returning resumed streams
- or make the transport/runtime handle this automatically for UI chunk streams
This was reproduced with DurableAgent streaming UI chunks directly to the chat client.
Summary
When using resumable streams with
WorkflowChatTransportand a negativeinitialStartIndex(for example-50), the reconnect response can start in the middle of a logical UI-message part sequence.That causes the AI SDK client stream processor to throw errors like:
Received reasoning-delta for missing reasoning part with ID "reasoning-0". Ensure a "reasoning-start" chunk is sent before any "reasoning-delta" chunks.This happened even though the reconnect endpoint correctly returned
x-workflow-stream-tail-indexas described in the docs.Versions
workflow:4.2.4@workflow/ai:4.1.2ai:6.0.168@ai-sdk/react:3.0.170What I expected
I expected negative resume indices to be safe to use for resumable chat streams, as long as:
initialStartIndex: -50x-workflow-stream-tail-indexgetRun(runId).getReadable({ startIndex })What actually happened
The reconnect stream started with deltas for an already-open logical part, instead of the matching start chunk.
Example from Chrome SSE output:
The AI SDK client then throws because it receives reasoning-delta before reasoning-start.
Why this seems surprising
The current docs around negative resume indices talk about transport/chunk offsets and x-workflow-stream-tail-index, but they do not mention that:
The AI SDK UI stream processor enforces those semantic boundaries, so raw chunk-index resume is not always safe for UI chunk replay.
Minimal setup
Client:
Reconnect route:
Workaround I had to implement
On the server, when startIndex < 0, I had to:
That fixed the issue.
Request
Can the docs and/or library behavior be clarified here?
Possible directions:
This was reproduced with DurableAgent streaming UI chunks directly to the chat client.