{"record":{"id":"37756fcbf3cf43fc","repo":"paperclipai/paperclip","slug":"accessdecision-explanation","errorCode":null,"errorMessage":"accessDecision.explanation","messagePattern":"accessDecision\\.explanation","errorType":"http","errorClass":"ToolGatewayHttpError","httpStatus":null,"severity":"error","filePath":"server/src/services/tool-gateway.ts","lineNumber":8140,"sourceCode":"      phase = \"denied\";\n    } else if (actionRequest.status === \"cancelled\") {\n      phase = \"cancelled\";\n    } else if (actionRequest.status === \"expired\") {\n      phase = \"expired\";\n    } else if (\n      actionRequest.status === \"approved\" ||\n      actionRequest.status === \"executed\"\n    ) {\n      phase = invocationDone ? \"done\" : \"running\";\n    } else {\n      phase = \"waiting\";\n    }\n\n    // Recover a redacted, structured snapshot of the parameters for the\n    // \"Where\" row — the test-call response never echoes them back.\n    let parameters: Record<string, unknown> | null = null;\n    const signed = readSignedToolArgumentsPayload({\n      signedArguments: actionRequest.signedArguments,\n      invocationId: invocation.id,\n      toolName: invocation.toolName,\n      signingSecret: options.toolActionSigningSecret,\n    });\n    if (\n      signed &&\n      signed.arguments &&\n      typeof signed.arguments === \"object\" &&\n      !Array.isArray(signed.arguments)\n    ) {\n      const redacted = validateToolContent({\n        value: signed.arguments,\n        direction: \"arguments\",\n        sensitiveMode: \"redact\",\n        promptInjectionMode: \"ignore\",\n      }).value;\n      if (\n        redacted &&","sourceCodeStart":8122,"sourceCodeEnd":8158,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/tool-gateway.ts#L8122-L8158","documentation":"Not a literal throw message: this is the audit details block written on tool_gateway.call_denied, where the access policy decision is recorded. The referenced field is accessDecision.explanation — the policy engine's human-readable denial reason (which policy matched and why), persisted alongside decision, reasonCode, and matchedPolicyIds for each denied call.","triggerScenarios":"Thrown at server/src/services/tool-gateway.ts:5797 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Access was denied by policy; read the decision explanation for the specific reason and adjust the request (scope, tool, or arguments) accordingly."],"exampleFix":null,"handlingStrategy":"try-catch","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"}