{"record":{"id":"c4f3dcded30a9d2a","repo":"paperclipai/paperclip","slug":"action-request-invalidated","errorCode":"action_request_invalidated","errorMessage":"Tool action request is no longer approvable; refresh the review queue","messagePattern":"Tool action request is no longer approvable; refresh the review queue","errorType":"http","errorClass":"ToolGatewayHttpError","httpStatus":409,"severity":"error","filePath":"server/src/services/tool-gateway.ts","lineNumber":7391,"sourceCode":"  }\n\n  async function markApprovedActionFailed(input: {\n    actionRequestId: string;\n    invocationId: string;\n    claimUpdatedAt: Date;\n    expectedInvocationStatus: \"awaiting_approval\" | \"executing\";\n    error: unknown;\n  }) {\n    const reasonCode =\n      input.error instanceof ToolGatewayHttpError\n        ? input.error.reasonCode\n        : \"tool_execution_failed\";\n    const message =\n      input.error instanceof Error ? input.error.message : String(input.error);\n    const now = new Date();\n    const settled = await db.transaction(async (tx) => {\n      // Lock in the same invocation -> request order used by the normal\n      // execution settlement path. The claim timestamp is the ownership token:\n      // a consumer that merely read an approved row cannot settle another\n      // consumer's claim, and a pre-dispatch failure cannot overwrite a call\n      // that has already entered provider execution or completed successfully.\n      const [invocation] = await tx\n        .select({ status: toolInvocations.status })\n        .from(toolInvocations)\n        .where(eq(toolInvocations.id, input.invocationId))\n        .for(\"update\")\n        .limit(1);\n      if (invocation?.status !== input.expectedInvocationStatus) return false;\n\n      const [actionRequest] = await tx\n        .select({\n          status: toolActionRequests.status,\n          updatedAt: toolActionRequests.updatedAt,\n        })\n        .from(toolActionRequests)\n        .where(eq(toolActionRequests.id, input.actionRequestId))","sourceCodeStart":7373,"sourceCodeEnd":7409,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/tool-gateway.ts#L7373-L7409","documentation":"Stale-row guard in approveToolAction: the signed arguments payload failed to load/verify (e.g. signing secret changed or payload corrupt) and the request is still pending, so it is cancelled and a 409 returned telling the reviewer the request is no longer approvable and the queue must be refreshed.","triggerScenarios":"Thrown at server/src/services/tool-gateway.ts:5266 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["The request left the approvable state. Refresh the review queue to get current actionable items."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}