vercel/ai · error · UIMessageStreamError

No tool invocation found for approval ID "${approvalId}".

Error message

No tool invocation found for approval ID "${approvalId}".

What it means

In process-ui-message-stream.ts, a 'tool-approval-response' chunk carries an approvalId; getToolInvocationByApprovalId searches the message's tool invocations for one whose `approval.id` matches. If no tool invocation has that approval ID, a UIMessageStreamError is thrown. The approval response references an approval request that does not exist in current message state.

Source

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

              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({
                chunkType: 'tool-approval-response',
                chunkId: approvalId,
                message: `No tool invocation found for approval ID "${approvalId}".`,
              });
            }

            return toolInvocation;
          }

          function updateToolPart(
            options: {
              toolName: keyof InferUIMessageTools<UI_MESSAGE> & string;
              toolCallId: string;
              providerExecuted?: boolean;
              title?: string;
              toolMetadata?: JSONObject;
            } & (
              | {

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure the tool invocation with `approval.id === approvalId` exists in the target message before emitting the approval response (keep messages containing the approval request).
  2. Use the approval object returned by the tool-approval-request chunk directly instead of manually constructed IDs.
  3. Check that approval responses target the correct message and are not split across conversations.
  4. Catch UIMessageStreamError for tool-approval-response chunks and surface a user-friendly 'approval no longer valid' state.

Example fix

// before
transport.sendMessages({ chatId, messages, trigger: 'tool-approval-response', ... }) // stale approvalId
// after
const approval = message.parts.find(p => p.type === 'tool-approval-request')?.approval;
// use approval.id from the live message part to build the response
Defensive patterns

Strategy: validation

Validate before calling

const target = messages
  .flatMap(m => m.parts)
  .find(p => p.type === 'tool' && p.approval?.id === approvalId);
if (!target) throw new Error(`No pending approval with id ${approvalId}`);

Type guard

function hasApproval(msg: UIMessage, approvalId: string): boolean {
  return msg.parts.some(p => p.type === 'tool' && p.approval?.id === approvalId);
}

Try / catch

try {
  await sendToolApprovalResponse({ approvalId, approved });
} catch (e) {
  if (e instanceof Error && /No tool invocation found for approval ID/.test(e.message)) {
    setApprovalState('expired'); // show 'approval no longer valid' UI
  } else throw e;
}

Prevention

When it happens

Trigger: Sending/processing a tool-approval-response chunk whose approvalId has no matching tool invocation (no prior tool-approval-request chunk, or the invocation was in a different message), e.g. when addToolApprovalResponse is called with a stale or wrong approval ID.

Common situations: Approving after the message list was trimmed/reset so the approval request no longer exists; copy-pasted or hardcoded approval IDs; multiple approval requests where the user's UI passed the wrong one; reconnect/replay dropping the approval-request chunk.

Related errors


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