vercel/ai · error · UIMessageStreamError

No tool invocation found for tool call ID "${toolCallId}".

Error message

No tool invocation found for tool call ID "${toolCallId}".

What it means

In process-ui-message-stream.ts, when a tool-result or related chunk arrives, getToolInvocation looks up the existing tool invocation by toolCallId in the message's toolInvocations. If none matches, it throws a UIMessageStreamError. The stream references a tool call that was never started (no tool-input-start / tool call part created first).

Source

Thrown at packages/ai/src/ui/process-ui-message-stream.ts:149

            let toolInvocation = toolInvocations.find(
              invocation => invocation.toolCallId === toolCallId,
            );

            if (toolInvocation == null) {
              const parts = state.message.parts;

              for (let i = parts.length - 1; i >= 0; i--) {
                const part = parts[i];
                if (isToolUIPart(part) && part.toolCallId === toolCallId) {
                  toolInvocation = part;
                  break;
                }
              }
            }

            if (toolInvocation == null) {
              throw new UIMessageStreamError({
                chunkType: 'tool-invocation',
                chunkId: toolCallId,
                message: `No tool invocation found for tool call ID "${toolCallId}".`,
              });
            }

            return toolInvocation;
          }

          function getToolInvocationByApprovalId(approvalId: string) {
            const toolInvocations = state.message.parts.filter(isToolUIPart);

            const toolInvocation = toolInvocations.find(
              invocation => invocation.approval?.id === approvalId,
            );

            if (toolInvocation == null) {
              throw new UIMessageStreamError({

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure the server always sends a 'tool-input-start' (tool call announcement) chunk before any tool-input-delta or tool-result chunks for that toolCallId.
  2. Verify toolCallIds are consistent between the stream chunks and any persisted message history being appended to.
  3. When resuming, replay or include the earlier tool-start chunks so state contains the invocation.
  4. Catch UIMessageStreamError in the stream processing and skip/handle orphan tool-result chunks.

Example fix

// before (server custom stream)
writer.write({ type: 'tool-result', toolCallId: 'call_1', result });
// after
writer.write({ type: 'tool-input-start', toolCallId: 'call_1', toolName: 'getWeather' });
writer.write({ type: 'tool-input-available', toolCallId: 'call_1', input });
writer.write({ type: 'tool-result', toolCallId: 'call_1', result });
Defensive patterns

Strategy: validation

Validate before calling

const exists = message.parts.some(
  p => p.type === 'tool' && p.toolCallId === toolCallId,
);
if (!exists) throw new Error(`Cannot emit result: unknown toolCallId ${toolCallId}`);

Type guard

function hasToolInvocation(msg: UIMessage, toolCallId: string): boolean {
  return msg.parts.some(
    (p): p is Extract<UIMessage['parts'][number], { type: 'tool' }> =>
      p.type === 'tool' && p.toolCallId === toolCallId,
  );
}

Try / catch

try {
  await processUIMessageStream(...);
} catch (e) {
  if (UIMessageStreamError.isInstance?.(e) || (e instanceof Error && /No tool invocation found for tool call ID/.test(e.message))) {
    console.warn('Orphan tool result chunk skipped', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: Consuming a UI message stream where a 'tool-input-available'/'tool-result' chunk carries a toolCallId that has no matching tool invocation in the message state — e.g. manually constructed streams, stream resumed mid-way dropping earlier chunks, or server sending results for calls it never announced.

Common situations: Custom hand-rolled stream writers on the server emitting tool results without tool-input-start; resuming a stream where the original tool-start chunks were already consumed; stream chunk loss in persistence/replay; mismatched toolCallIds after server-side regeneration.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/bceea9b54e454f83. Report an issue: GitHub.