paperclipai/paperclip · error · ToolGatewayHttpError
action_already_consumed
action_already_consumed
Error message
Tool action request was already consumed
What it means
Idempotency guard in executeApprovedAgentInvocation: the compare-and-set from approved to executing matched no rows, meaning another executor already consumed this approval (or it was resolved otherwise) and the wait-for-settlement fallback also found it neither executed nor failed. The request was already consumed and cannot run twice.
Solutions
- This action request was already consumed by a prior execution. Create a new approval request instead of reusing it.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at server/src/services/tool-gateway.ts:4177 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/8c7d173f4230f5a3.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/tool-gateway.ts:7626
if (!claimed) {
const settled = await waitForActionRequestExecution(actionRequest.id);
const [settledInvocation] = await db
.select()
.from(toolInvocations)
.where(eq(toolInvocations.id, invocation.id))
.limit(1);
if (settled?.status === "executed" && settledInvocation) {
return storedInvocationResult(settledInvocation);
}
if (settled?.status === "failed") {
throw new ToolGatewayHttpError(
502,
settledInvocation?.errorMessage ?? "Approved tool action failed",
settledInvocation?.errorCode ?? "tool_execution_failed",
{ actionRequestId: actionRequest.id, invocationId: invocation.id },
);
}
throw new ToolGatewayHttpError(
409,
"Tool action request was already consumed",
"action_already_consumed",
);
}
// Terminal-issue expiry revokes pending/approved requests, but a claim that
// committed just before the issue closed slips past that revocation. Recheck
// the issue after winning the claim so a governed action never runs external
// side effects for an issue that is already done or cancelled.
const issue = await assertIssueOpenForApprovedAction({
claimed,
invocation,
});
const signedPayload = readSignedToolArgumentsPayload({
signedArguments: claimed.signedArguments,
invocationId: invocation.id,View on GitHub (pinned to 3f1d897a7c)