{"record":{"id":"d675409e84fcedac","repo":"vercel/ai","slug":"workflowchattransport-dropping-orphan-ui-chunk","errorCode":null,"errorMessage":"[WorkflowChatTransport] Dropping orphan UI chunk (${orphanKind} for id \"${orphanRef}\") on resume — the resume position landed mid-part. The dropped chunk(s) reference a part whose start chunk wasn't in the resumed window. To preserve the full message, configure your stream endpoint to rewind to a step boundary before returning the readable. See: https://workflow.dev/docs/ai/resumable-streams#mid-part-resumes","messagePattern":"\\[WorkflowChatTransport\\] Dropping orphan UI chunk \\((.+?) for id \"(.+?)\"\\) on resume — the resume position landed mid-part\\. The dropped chunk\\(s\\) reference a part whose start chunk wasn't in the resumed window\\. To preserve the full message, configure your stream endpoint to rewind to a step boundary before returning the readable\\. See: https://workflow\\.dev/docs/ai/resumable-streams#mid-part-resumes","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/workflow/src/workflow-chat-transport.ts","lineNumber":48,"sourceCode":" * part from those directly), so all three mark the call id as seen.\n *\n * This is a best-effort safety net — it preserves only the parts that the\n * resumed window includes a `*-start` for. Server-side rewinding to a step\n * boundary is the proper fix when you want the full message preserved.\n */\ntype OrphanFilter = {\n  shouldDrop: (chunk: UIMessageChunk) => boolean;\n};\n\nfunction createOrphanFilter(): OrphanFilter {\n  const seenStartedIds = new Set<string>();\n  const seenStartedToolCallIds = new Set<string>();\n  let warnedOnce = false;\n\n  function warnOnce(orphanKind: string, orphanRef: string) {\n    if (warnedOnce) return;\n    warnedOnce = true;\n    console.warn(\n      '[WorkflowChatTransport] Dropping orphan UI chunk ' +\n        `(${orphanKind} for id \"${orphanRef}\") on resume — ` +\n        'the resume position landed mid-part. The dropped chunk(s) ' +\n        \"reference a part whose start chunk wasn't in the resumed \" +\n        'window. To preserve the full message, configure your ' +\n        'stream endpoint to rewind to a step boundary before ' +\n        'returning the readable. See: ' +\n        'https://workflow.dev/docs/ai/resumable-streams#mid-part-resumes',\n    );\n  }\n\n  function shouldDrop(chunk: UIMessageChunk): boolean {\n    switch (chunk.type) {\n      case 'reset-step':\n        seenStartedIds.clear();\n        seenStartedToolCallIds.clear();\n        return false;\n      case 'text-start':","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/workflow/src/workflow-chat-transport.ts#L30-L66","documentation":"When resuming a UI message stream, chunks can reference a message part whose start chunk was not included in the resumed window (an 'orphan' chunk). The transport drops such chunks so the UI isn't corrupted, and warns once (first occurrence only) that the resume landed mid-part, with a doc link on how to avoid it.","triggerScenarios":"Calling reconnectToStream / resuming a chat where the server's reconnection endpoint returns a stream whose first chunks are continuation chunks (e.g. text-delta, tool-output updates) without the preceding part-start chunk (e.g. text-start, tool-input-start) for the same id.","commonSituations":"Custom stream endpoints that resume from an arbitrary chunk index rather than a step boundary; servers trimming persisted stream logs; clients reconnecting after a mid-part disconnect.","solutions":["Configure the stream endpoint to rewind to a step boundary before returning the readable, so a part-start chunk always begins the resumed window.","Include the part-start chunks in the resumed stream window (start the stream earlier).","Persist the full stream server-side and replay from the last complete step boundary.","Accept the drop if losing the partially-started part is acceptable."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Resume streams from step boundaries, never mid-part","Persist full stream logs server-side for replay","Test disconnect/reconnect mid-stream scenarios against your endpoint"],"tags":["streaming","resume","workflow","ui"],"backgroundTag":"mid-part-stream-resume","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}