can1357/oh-my-pi · warning · Error
Tool call denied by user: ${this.tool.name}
Error message
Tool call denied by user: ${this.tool.name} What it means
The user was shown the approval prompt for this tool call and explicitly chose not to approve it. The wrapper emits an approval-resolved event with reason 'denied by user' and throws so the tool call is recorded as an error rather than executed.
Source
Thrown at packages/coding-agent/src/extensibility/extensions/wrapper.ts:340
}
const uiContext = this.runner.getUIContext();
const basePrompt = formatApprovalPrompt(this.tool, resolvedArgs, approvalCheck.reason);
const safetyPrompt =
pendingSafetyChecks.length > 0
? `${basePrompt}\nProvider safety checks:\n${safetyCheckLines(pendingSafetyChecks).join("\n")}`
: basePrompt;
let choice: string | undefined;
try {
choice = await uiContext.select(safetyPrompt, ["Approve", "Deny"]);
} catch (err) {
await emitApprovalResolved(false, err instanceof Error ? err.message : "approval aborted");
throw err;
}
const approved = choice === "Approve";
await emitApprovalResolved(approved, approved ? undefined : "denied by user");
if (!approved) {
throw new Error(`Tool call denied by user: ${this.tool.name}`);
}
if (pendingSafetyChecks.length > 0) {
if (!context) throw new Error("Provider safety approval context is unavailable");
context.providerSafetyApproved = true;
}
}
// Execute the actual tool
let result: AgentToolResult<TDetails, TParameters>;
let executionError: Error | undefined;
try {
// A denied file write or delete inside this tool can be brokered to an
// extension handler, and that registry is PROCESS-WIDE — so the session is
// named here, the one place where every tool's execution and the runner
// that owns the handlers are both in scope (`sdk.ts` wraps the whole tool
// registry with this class whenever a runner exists). Inert with no
// fallback registered: no scope is entered.View on GitHub (pinned to 9690622007)
Solutions
- If denial was accidental, re-issue the tool call and approve it
- Pre-approve the tool via tools.approval.<toolName>: allow if prompts are too frequent
- Adjust approvalMode to reduce prompting in trusted environments
- Catch/handle the error upstream and instruct the agent to try a different approach
Defensive patterns
Strategy: try-catch
Type guard
function isUserDenial(e: unknown): boolean { return e instanceof Error && e.message.startsWith('Tool call denied by user:'); } Try / catch
try { await tool.execute(params); } catch (err) {
if (isUserDenial(err)) {
return { status: 'denied', tool: err.message.split(': ')[1] };
}
throw err;
} Prevention
- Treat denial as an expected control-flow outcome, not a crash
- Instruct agents to propose alternative approaches after a denial
- Use allow rules to cut prompt fatigue so users deny less by accident
- Emit approval-resolved telemetry to track denial rates
When it happens
Trigger: A user presses 'Deny' (or aborts via Esc producing a rejection that resolves to non-Approve) on the tool approval dialog during execution in an interactive UI.
Common situations: Agent attempts a destructive or unexpected command and the user declines; automated flows where a human in the loop denies an action; scripts driving the TUI that answer 'Deny'.
Related errors
- Kimi device authorization denied
- ${reason}
- Extension failed, blocking execution: ${String(err)}
- Tool "${this.tool.name}" has pending provider safety checks
- Tool "${this.tool.name}" requires approval but no interactiv
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2340d4d7b21b7fe8.
Report an issue: GitHub.