paperclipai/paperclip · error · Error

paperclip_runner_attachment_staging_not_authorized

paperclip_runner_attachment_staging_not_authorized

Error message

paperclip_runner_attachment_staging_not_authorized

What it means

Authorization gate for staging runner attachment bytes: the run must exist and its agentStatus must not be paused, terminated, pending_approval, or error. Staging is only permitted for actively running agents; a missing run row or a halted lifecycle state rejects the handoff so paused/failed runs cannot inject files.

Solutions

  1. Check the run's current agentStatus before staging and only stage while the run is active
  2. Resume/restart the run (resolve pause or approval) and then retry the attachment handoff
  3. Verify the runId/binding is correct — a missing run row also produces this error
Defensive patterns

Strategy: try-catch

Validate before calling

const run = await getRun(runId);
if (!run || ["paused","terminated","pending_approval","error"].includes(run.agentStatus)) {
  throw new Error(`run ${runId} not stageable (status=${run?.agentStatus ?? "missing"})`);
}

Type guard

const isStageableRun = (run) => !!run && !["paused","terminated","pending_approval","error"].includes(run.agentStatus);

Try / catch

try { await stageAttachmentForRun(input); } catch (e) { if (e.message === "paperclip_runner_attachment_staging_not_authorized") { /* stop streaming, wait for resume, or abort the handoff */ } else throw e; }

Prevention

When it happens

Trigger: Calling the attachment staging entrypoint when the run row is absent (unknown/expired runId), or run.agentStatus is 'paused', 'terminated', 'pending_approval', or 'error'.

Common situations: Runner keeps streaming attachments after the task hit a budget hard-stop (paused); staging attempted during an approval gate (pending_approval); stale runner retries after the run errored or was terminated; wrong runId passed by the caller.

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


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/522d910be26af01d. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/native-runtime/native-runner-file-handoff.ts:859

        eq(heartbeatRuns.agentId, input.binding.agentId),
        eq(heartbeatRuns.nativeIssueId, input.binding.issueId),
        eq(heartbeatRuns.runtimeMode, "native"),
        inArray(heartbeatRuns.status, ["queued", "running"]),
        eq(issues.id, input.binding.issueId),
        eq(issues.companyId, input.binding.companyId),
        eq(issues.executionRunId, input.binding.runId),
        eq(agents.id, input.binding.agentId),
        eq(agents.companyId, input.binding.companyId),
      ),
    )
    .limit(1);
  if (
    !run ||
    ["paused", "terminated", "pending_approval", "error"].includes(
      run.agentStatus,
    )
  ) {
    throw new Error("paperclip_runner_attachment_staging_not_authorized");
  }
  const reviewContext = readNativeReviewAssignmentContext(run.contextSnapshot);
  const nativeReview = reviewContext
    ? await getNativeReviewAssignment(input.db, {
        companyId: input.binding.companyId,
        issueId: input.binding.issueId,
        agentId: input.binding.agentId,
        contextSnapshot: reviewContext,
      })
    : null;
  if (run.assigneeAgentId !== input.binding.agentId && !nativeReview) {
    throw new Error("paperclip_runner_attachment_staging_not_authorized");
  }
  const selections = wakeAttachmentSelections(run.contextSnapshot);
  if (selections.length > MAX_NATIVE_STAGED_ATTACHMENTS) {
    throw new Error("paperclip_runner_attachment_staging_count_denied");
  }
  const workspaceRoot =

View on GitHub (pinned to 3f1d897a7c)