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
- Check the agent's current status and resume/reactivate it (clear paused/error, complete approval) before requesting the file handoff
- Re-issue the handoff after the pending approval is granted
- Verify the identifier used to load the agent context is correct so the context is not null
- 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
- Check agent status before scheduling file handoffs; gate handoffs on active states only
- Resolve pending approvals before requesting handoffs
- Surface paused/terminated state in runner UI so operators re-enable agents proactively
- Handle null context explicitly instead of passing an unresolved lookup result onward
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
- paperclip_runner_chat_attachment_principal_denied
- paperclip_runner_chat_attachment_read_scope_unavailable
- paperclip_runner_chat_attachment_source_denied
- paperclip_runner_tool_binding_not_authorized
- paperclip_runner_tool_binding_not_authorized
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)