shadcn-ui/ui · error
A needsApproval tool call resolves from the user's decision;
Error message
A needsApproval tool call resolves from the user's decision; remove the ${method}() call. What it means
In the chat writer, a tool created with needsApproval: true is resolved by the user's approve/deny decision, not by the script. assertResolvable() blocks calling output(), error(), or denied() on such a handle because the approval flow owns the resolution and supplies the result via the approval state.
Source
Thrown at packages/helpers/src/core/writer.ts:162
if (clonedOptions.toolCallId !== undefined) {
context.ids.reserveToolCallId(clonedOptions.toolCallId)
}
events.push({
kind: "tool-input",
name,
toolCallId,
title: clonedOptions.title,
toolMetadata: clonedOptions.toolMetadata,
providerExecuted: clonedOptions.providerExecuted,
dynamic: clonedOptions.dynamic,
input: clonedOptions.input ?? {},
})
function assertResolvable(method: string) {
if (clonedOptions.needsApproval) {
throw new Error(
`A needsApproval tool call resolves from the user's decision; remove the ${method}() call.`
)
}
}
const handle: ToolHandle<TOOLS[NAME]["output"]> = {
sleep(delayMs: number) {
events.push({
kind: "sleep",
delayMs,
phase: "after-start",
})
return handle
},
output(output: TOOLS[NAME]["output"]) {
assertResolvable("output")View on GitHub (pinned to efac598707)
Solutions
- Remove the .output()/.error()/.denied() call on needsApproval tool handles.
- Let the approval flow provide output via approval.output.
- If you must script the output yourself, set needsApproval: false.
Example fix
// before
writer.tool("deleteFile", { needsApproval: true, input }).output({ ok: true })
// after
writer.tool("deleteFile", { needsApproval: true, input })
// output is supplied by the user's approval decision Defensive patterns
Strategy: validation
Validate before calling
// branch on needsApproval before scripting a resolution
const handle = writer.tool(name, options)
if (!options.needsApproval) {
handle.output(result)
} Type guard
const isApprovalTool = (
opts: { needsApproval?: boolean }
): opts is { needsApproval: true } => Boolean(opts.needsApproval) Prevention
- Never chain output/error/denied on a needsApproval tool handle.
- Document per-tool whether it is approval-gated.
When it happens
Trigger: Scripting writer.tool("x", { needsApproval: true }).output(...) (or .error()/.denied()) in the same turn; converting a normal tool to approval-gated without removing the explicit resolution call.
Common situations: Misunderstanding the approval lifecycle; copy-pasting a non-approval tool script and adding needsApproval.
Related errors
- No assistant response found for this transcript.
- Only continuation turns materialize from a transcript.
- count must be a non-negative integer.
- get() cannot materialize a continuation turn without a live
- No assistant response found for this transcript.
AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12).
Data as JSON: /api/errors/bbba8941fecdb52d.
Report an issue: GitHub.