paperclipai/paperclip · error · Error

paperclip_runner_file_handoff_not_authorized

paperclip_runner_file_handoff_not_authorized

Error message

paperclip_runner_file_handoff_not_authorized

What it means

Thrown by the file-handoff authorization check when there is no agent execution context, or the agent's status is one of paused, terminated, pending_approval, or error. A file handoff to the native runner is only allowed for agents in an active state, so handoff preparation is refused otherwise.

Solutions

  1. Check the agent's current status and resume/reactivate it (clear paused/error, complete approval) before requesting the file handoff
  2. Re-issue the handoff after the pending approval is granted
  3. Verify the identifier used to load the agent context is correct so the context is not null
  4. If termination was intentional, start a new run instead of handing off files to the terminated agent

Example fix

// before
await prepareNativeRunnerFileHandoff({ db, context }); // agent paused
// after
if (context && !"paused terminated pending_approval error".split(" ").includes(context.agent.status)) {
  await prepareNativeRunnerFileHandoff({ db, context });
} else {
  throw new Error(`handoff blocked: agent status is ${context?.agent.status ?? "unknown"}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const status = context?.agent.status;
const handoffAllowed = !!context && !["paused", "terminated", "pending_approval", "error"].includes(status);

Type guard

function handoffReady(c: { agent: { status: string } } | null | undefined): c is { agent: { status: "queued" | "running" } } {
  return !!c && !["paused", "terminated", "pending_approval", "error"].includes(c.agent.status);
}

Try / catch

try {
  await prepareNativeRunnerFileHandoff({ db, context });
} catch (err) {
  if (err instanceof Error && err.message === "paperclip_runner_file_handoff_not_authorized") {
    // inspect context.agent.status, resume agent or await approval, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: Calling prepareNativeRunnerFileHandoff (or its underlying authorization helper) when the loaded agent context is null/undefined, or context.agent.status is exactly "paused", "terminated", "pending_approval", or "error".

Common situations: Requesting a file handoff after an operator paused the agent; retrying handoff after a previous run errored or the agent was terminated; hitting the handoff path while the run is blocked awaiting approval; passing a bad identifier so the context lookup returns nothing.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

        eq(heartbeatRuns.runtimeMode, "native"),
        eq(heartbeatRuns.status, "running"),
        eq(issues.id, binding.issueId),
        eq(issues.companyId, binding.companyId),
        eq(issues.assigneeAgentId, binding.agentId),
        eq(issues.executionRunId, binding.runId),
        eq(agents.id, binding.agentId),
        eq(agents.companyId, binding.companyId),
      ),
    )
    .for("update")
    .limit(1);
  if (
    !context ||
    ["paused", "terminated", "pending_approval", "error"].includes(
      context.agent.status,
    )
  ) {
    throw new Error("paperclip_runner_file_handoff_not_authorized");
  }
  return { statusVersion: context.issue.statusVersion };
}

export async function prepareNativeRunnerFileHandoff(input: {
  readonly db: Db;
  readonly binding: NativeRunnerFileHandoffBinding;
  readonly deliverable: NativeRunnerFileHandoffInput;
  readonly storage?: StorageService;
}): Promise<PreparedNativeRunnerFileHandoff> {
  const { statusVersion } = await assertCurrentBinding(input.db, input.binding);
  const verified = await readVerifiedWorkspaceFile(
    input.binding,
    input.deliverable,
  );

  const [existing] = await input.db
    .select({

View on GitHub (pinned to 3f1d897a7c)