vercel/ai · error · Error
Could not find tool approval request ${request.approvalId}.
Error message
Could not find tool approval request ${request.approvalId}. What it means
applyToolApprovalResponse locates the tool part referenced by an approval request (by partIndex and toolCallId) before writing the approval response. If the part is missing, is not a tool part, or the toolCallId no longer matches, it throws. This protects against applying a stale approval to the wrong tool call.
Source
Thrown at packages/tui/src/agent-tui-runner.ts:658
input: part.input,
providerExecuted: part.providerExecuted,
messageId: message.id,
partIndex: index,
});
}
return requests;
}
function applyToolApprovalResponse(
message: UIMessage,
request: AgentTUIToolApprovalRequest,
response: AgentTUIToolApprovalResponse,
) {
const part = message.parts[request.partIndex];
if (!part || !isToolUIPart(part) || part.toolCallId !== request.toolCallId) {
throw new Error(
`Could not find tool approval request ${request.approvalId}.`,
);
}
part.state = 'approval-responded';
part.approval = {
id: request.approvalId,
approved: response.approved,
...(response.reason ? { reason: response.reason } : {}),
};
}
function formatStreamError(error: unknown) {
if (error instanceof Error) {
return error.message;
}
if (typeof error === 'string') {View on GitHub (pinned to 69428b1f8b)
Solutions
- Apply the approval response once, against the current message state.
- Re-derive requests from the latest responseMessage instead of caching them.
- Guard the stored request list and clear it after responding.
- Match by toolCallId against current parts before calling applyToolApprovalResponse.
Example fix
// before const request = staleRequests[0]; applyToolApprovalResponse(message, request, response); // after const current = findPendingToolApprovalRequests(latestMessage) .find(r => r.approvalId === approvalId); if (current) applyToolApprovalResponse(latestMessage, current, response);
Defensive patterns
Strategy: validation
Validate before calling
function isRequestStillValid(message, request) {
const part = message.parts[request.partIndex];
return Boolean(part && isToolUIPart(part) && part.toolCallId === request.toolCallId);
} Type guard
function isPendingApproval(message, request) {
const part = message.parts[request.partIndex];
return Boolean(
part && isToolUIPart(part) && part.toolCallId === request.toolCallId,
);
} Try / catch
try {
applyToolApprovalResponse(message, request, response);
} catch (error) {
if (error.message.includes('Could not find tool approval request')) {
// refresh requests from the latest message and retry once
} else throw error;
} Prevention
- Always re-read requests from the latest message state.
- Respond to each approval exactly once.
- Clear pending request lists after responding.
- Avoid caching approval requests across streaming updates.
When it happens
Trigger: Responding to an AgentTUIToolApprovalRequest after the message's parts changed — e.g. the message was updated/reset, the part index shifted, or the response is applied twice.
Common situations: Duplicate approval responses; stale requests captured before a rerender; messages replaced during streaming so partIndex is outdated; applying responses out of order.
Related errors
- No tool invocation found for approval ID "${approvalId}".
- No prompt was provided and the renderer does not support pro
- Tool approval was requested, but the renderer does not suppo
- 'element streams in no-schema mode' functionality not suppor
- 'element streams in object mode' functionality not supported
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/7563b912b9b634b6.
Report an issue: GitHub.