paperclipai/paperclip · error · ToolGatewayHttpError

approval_required

approval_required

Error message

Tool action requires approval

What it means

Approval-required flow: this helper builds the agent-facing instruction text returned with the 409/deferral when a governed tool call needs human approval. It resolves the issue identifier (falling back to the raw issueId) so the agent knows which task's approval card to wait on and to end its run in_review instead of retrying.

Solutions

  1. Request approval for the tool action (create an approval request) and wait for board approval before executing.
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at server/src/services/tool-gateway.ts:1483 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/9737cc51ac26ae09. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/tool-gateway.ts:2145

  ): Promise<string> {
    const [issue] = await db
      .select({ identifier: issues.identifier })
      .from(issues)
      .where(eq(issues.id, issueId))
      .limit(1);
    const task = issue?.identifier ?? issueId;
    return `A human approval card was posted on task ${task}. Do not retry this call now. Wrap up other work and end your run noting you are waiting on tool approval (status in_review). You will be woken when it is decided; if approved, the action runs automatically and your wake includes the result.`;
  }

  async function throwApprovalRequired(input: {
    invocationId: string;
    actionRequestId: string;
    interactionId?: string | null;
    issueId: string;
    toolName: string;
    argumentsHash: string;
  }): Promise<never> {
    throw new ToolGatewayHttpError(
      409,
      "Tool action requires approval",
      "approval_required",
      {
        invocationId: input.invocationId,
        actionRequestId: input.actionRequestId,
        interactionId: input.interactionId ?? null,
        issueId: input.issueId,
        tool: input.toolName,
        argumentsHash: input.argumentsHash,
        instructions: await approvalRequiredInstructions(input.issueId),
      },
    );
  }

  async function requestApprovalForRecordedToolCall(input: {
    invocation: typeof toolInvocations.$inferSelect;
    actionRequest: typeof toolActionRequests.$inferSelect | null;

View on GitHub (pinned to 3f1d897a7c)