{"record":{"id":"7563b912b9b634b6","repo":"vercel/ai","slug":"could-not-find-tool-approval-request-request-app","errorCode":null,"errorMessage":"Could not find tool approval request ${request.approvalId}.","messagePattern":"Could not find tool approval request (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tui/src/agent-tui-runner.ts","lineNumber":658,"sourceCode":"      input: part.input,\n      providerExecuted: part.providerExecuted,\n      messageId: message.id,\n      partIndex: index,\n    });\n  }\n\n  return requests;\n}\n\nfunction applyToolApprovalResponse(\n  message: UIMessage,\n  request: AgentTUIToolApprovalRequest,\n  response: AgentTUIToolApprovalResponse,\n) {\n  const part = message.parts[request.partIndex];\n\n  if (!part || !isToolUIPart(part) || part.toolCallId !== request.toolCallId) {\n    throw new Error(\n      `Could not find tool approval request ${request.approvalId}.`,\n    );\n  }\n\n  part.state = 'approval-responded';\n  part.approval = {\n    id: request.approvalId,\n    approved: response.approved,\n    ...(response.reason ? { reason: response.reason } : {}),\n  };\n}\n\nfunction formatStreamError(error: unknown) {\n  if (error instanceof Error) {\n    return error.message;\n  }\n\n  if (typeof error === 'string') {","sourceCodeStart":640,"sourceCodeEnd":676,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/tui/src/agent-tui-runner.ts#L640-L676","documentation":"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.","triggerScenarios":"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.","commonSituations":"Duplicate approval responses; stale requests captured before a rerender; messages replaced during streaming so partIndex is outdated; applying responses out of order.","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."],"exampleFix":"// before\nconst request = staleRequests[0];\napplyToolApprovalResponse(message, request, response);\n// after\nconst current = findPendingToolApprovalRequests(latestMessage)\n  .find(r => r.approvalId === approvalId);\nif (current) applyToolApprovalResponse(latestMessage, current, response);","handlingStrategy":"validation","validationCode":"function isRequestStillValid(message, request) {\n  const part = message.parts[request.partIndex];\n  return Boolean(part && isToolUIPart(part) && part.toolCallId === request.toolCallId);\n}","typeGuard":"function isPendingApproval(message, request) {\n  const part = message.parts[request.partIndex];\n  return Boolean(\n    part && isToolUIPart(part) && part.toolCallId === request.toolCallId,\n  );\n}","tryCatchPattern":"try {\n  applyToolApprovalResponse(message, request, response);\n} catch (error) {\n  if (error.message.includes('Could not find tool approval request')) {\n    // refresh requests from the latest message and retry once\n  } else throw error;\n}","preventionTips":["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."],"tags":["tui","tool-approval","state-mismatch","api-misuse"],"backgroundTag":"stale-state-reference","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}