{"record":{"id":"67fc4524594b2c4e","repo":"vercel/ai","slug":"code-mode-tool-approval-denied","errorCode":"CODE_MODE_TOOL_APPROVAL_DENIED","errorMessage":"Tool \"${toolName}\" approval was denied${reason ? `: ${reason}` : '.'}","messagePattern":"Tool \"(.+?)\" approval was denied(.+?)` : '\\.'\\}","errorType":"exception","errorClass":"CodeModeToolApprovalDeniedError","httpStatus":null,"severity":"warning","filePath":"packages/code-mode/src/run-code-mode.ts","lineNumber":291,"sourceCode":"    input.toolExecutionOptions?.context;\n  const baseExecutionOptions: CodeModeToolExecutionOptions = {\n    toolCallId: outerToolCallId,\n    messages: input.toolExecutionOptions?.messages ?? [],\n    abortSignal: context.abortSignal,\n    ...(forwardedContext === undefined ? {} : { context: forwardedContext }),\n    ...(forwardedExperimentalContext === undefined\n      ? {}\n      : { experimental_context: forwardedExperimentalContext }),\n  };\n\n  let codeModeInterrupt: CodeModeInterruptExecutionContext | undefined;\n  let skipApproval = false;\n  if (context.resume !== undefined) {\n    const payload = assertInterruptPayload(context.resume.payload);\n    if (isCodeModeApprovalInterruptPayload(payload)) {\n      const decision = normalizeApprovalResolution(context.resume.resolution);\n      if (!decision.approved) {\n        throw new CodeModeToolApprovalDeniedError(\n          toolName,\n          toolInput,\n          toolCallId,\n          decision.reason,\n        );\n      }\n      skipApproval = true;\n    } else {\n      codeModeInterrupt = {\n        interruptId: `${toolCallId}:interrupt`,\n        payload,\n        resolution: context.resume.resolution,\n      };\n    }\n  }\n\n  try {\n    const inputJson = toJsonPayload(","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/code-mode/src/run-code-mode.ts#L273-L309","documentation":"When a code-mode run resumes from an approval interrupt, invokeCodeModeTool normalizes the stored resolution and, if it records approval as denied, throws CodeModeToolApprovalDeniedError carrying the tool name, input, tool call id, and the denial reason. This surfaces the human's 'no' decision back into the sandboxed program so it can handle the rejection instead of silently executing the tool.","triggerScenarios":"Resuming a code-mode run via continueCodeModeInterrupt (or interruptResolution) where the resolution for an approval-kind interrupt is { approved: false, reason } — e.g. a user clicked 'Deny' in an approval UI.","commonSituations":"Human-in-the-loop approval flows where reviewers reject sensitive tool calls (deleting data, spending money); automated policy gates that deny approvals outside business hours; applications that resume with a default 'deny' resolution after a timeout.","solutions":["This is an expected, intended outcome — catch CodeModeToolApprovalDeniedError (isInstance) in the host and in sandbox code handle the denial branch.","If the denial was accidental, re-issue a fresh run/approval request and resume with { approved: true }.","Include a clear reason in the resolution so sandbox code and logs can distinguish denial causes."],"exampleFix":"// before\nawait continueCodeModeInterrupt({ interrupt, resolution: decision, tools }); // crashes on deny\n// after\ntry {\n  await continueCodeModeInterrupt({ interrupt, resolution: decision, tools });\n} catch (error) {\n  if (CodeModeToolApprovalDeniedError.isInstance(error)) {\n    return { status: 'denied', reason: error.reason };\n  }\n  throw error;\n}","handlingStrategy":"try-catch","validationCode":"// Before resuming, inspect the resolution you are about to submit:\nif (resolution && resolution.approved === false) {\n  // expect a CodeModeToolApprovalDeniedError; branch your UI accordingly\n}","typeGuard":"function isDenied(resolution: unknown): boolean {\n  return typeof resolution === 'object' && resolution !== null && (resolution as { approved?: unknown }).approved === false;\n}","tryCatchPattern":"try {\n  return await continueCodeModeInterrupt({ interrupt, resolution, tools });\n} catch (error) {\n  if (CodeModeToolApprovalDeniedError.isInstance(error)) {\n    return { status: 'denied', toolName: error.toolName, reason: error.reason };\n  }\n  throw error;\n}","preventionTips":["Treat denial as a first-class outcome in human-in-the-loop flows, not an exception path.","Record the denial reason in the resolution for audit logs.","In sandbox code, wrap tool calls in try/catch to handle denied approvals gracefully."],"tags":["code-mode","approval-denied","human-in-the-loop","interrupt"],"backgroundTag":"tool-approval-denied","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}