paperclipai/paperclip · error · ToolGatewayHttpError
currentAccess.reasonCode
currentAccess.reasonCode
Error message
currentAccess.explanation
What it means
This 403 ToolGatewayHttpError is thrown during execution of an approved (execute-on-approve) tool action, after the request claim is won and the signed arguments are verified. Before running the side effect, the policy service is re-consulted (policyService.decide) with the current session, tool, and parameters; if the live policy decision is not allowed and not 'require_approval', the action is aborted with the policy's explanation and reasonCode, and the action request is marked failed. This enforces that policy revocations between approval time and execution time are honored.
Solutions
- Read currentAccess.explanation/reasonCode in the error to see which policy rule denied the action, then adjust that policy (or the tool/connection config) and have the agent re-request approval.
- Re-request the tool action so a fresh approval is created and decided under the current policy.
- If the deny is unintended, update the tool policy for the company/agent to allow the tool class, then retry the invocation.
- Check whether the tool or its remote connection definition changed after approval; if so a new review is required regardless.
Example fix
// before: policy denies post-approval, action fails
await executeApprovedAction(actionRequestId);
// after: verify policy before requesting approval to avoid wasted approvals
const access = await policyService.decide(policyInputForTool({ session, tool, parameters }));
if (!access.allowed && access.decision !== "require_approval") throw new Error(`Policy denies tool: ${access.explanation}`);
await executeApprovedAction(actionRequestId); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: re-check policy before requesting approval
const access = await policyService.decide(policyInputForTool({ session, tool, parameters }));
if (!access.allowed && access.decision !== "require_approval") {
throw new Error(`Tool policy denies this action: ${access.explanation}`);
} Type guard
function isPolicyAllowed(access: { allowed: boolean; decision: string }): boolean {
return access.allowed || access.decision === "require_approval";
} Try / catch
try {
await executeApprovedAction(actionRequestId);
} catch (err) {
if (err?.status === 403 && typeof err?.code === "string") {
// policy changed between approval and execution; mark request failed and re-request
await requestFreshApproval({ toolName, parameters, note: err.message });
} else throw err;
} Prevention
- Avoid editing tool policies while approvals are pending; batch policy changes between review cycles.
- Keep approval queues short so the gap between approval and execution stays small.
- Log reasonCode/explanation from 403s to spot which policy rules repeatedly block actions.
- Re-validate policy at request time so stale requests are rejected early.
When it happens
Trigger: Executing a claimed approved tool action where the company/agent tool policy has changed since approval: the tool was moved to a denied policy class, the agent lost permission for the tool or connection, budget/domain/parameter-level policy rules now deny the call, or the identity context restored for the action is no longer permitted the tool.
Common situations: An operator tightened tool permissions while the approval sat in the queue; the approval was granted under one policy snapshot but a policy update landed before execution; a remote MCP connection's permission set changed so the re-decision denies; token/role downgrades on the agent between approve and execute.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- access.reasonCode
- paperclip_runner_chat_attachment_read_not_authorized
- Run telemetry is outside this actor's authorization boundary
- Runtime service control is outside this actor's…
- trustPreset.detail
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/27d91e4da80c7c2d.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/tool-gateway.ts:7720
signedPayload.identityContextId,
);
tool = await findToolForSession(session, invocation.toolName);
liveApprovalSnapshot = await connectedRemoteApprovalSnapshot(
session,
tool,
);
const currentAccess = await policyService.decide(
policyInputForTool({
session,
tool,
parameters: signedPayload.arguments,
}),
);
if (
!currentAccess.allowed &&
currentAccess.decision !== "require_approval"
)
throw new ToolGatewayHttpError(
403,
currentAccess.explanation,
currentAccess.reasonCode,
);
} catch (error) {
await markApprovedActionFailed({
actionRequestId: claimed.id,
invocationId: invocation.id,
claimUpdatedAt: claimed.updatedAt,
expectedInvocationStatus: "awaiting_approval",
error,
});
throw error;
}
if (
!approvalSnapshotsMatch(
signedPayload.approvalSnapshot,
liveApprovalSnapshot,View on GitHub (pinned to 3f1d897a7c)