{"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":5434,"sourceCode":"      error: invocation?.errorMessage ?? null,\n    };\n  }\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 = input.error instanceof ToolGatewayHttpError\n      ? input.error.reasonCode\n      : \"tool_execution_failed\";\n    const message = 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({ status: toolActionRequests.status, updatedAt: toolActionRequests.updatedAt })\n        .from(toolActionRequests)\n        .where(eq(toolActionRequests.id, input.actionRequestId))\n        .for(\"update\")\n        .limit(1);\n      if (","sourceCodeStart":5416,"sourceCodeEnd":5452,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/tool-gateway.ts#L5416-L5452","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":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}