paperclipai/paperclip · error
native_runner_prp_run_rotation_failed
Error message
native_runner_prp_run_rotation_failed
What it means
attachRun sends a 'run.attach' command to the runner core and waits for it to complete. If the command finishes with any status other than 'completed', the run rotation failed and this error is thrown. It signals the runner rejected or failed the durable identity rotation for the run.
Source
Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:3660
this.#authorizedTools,
this.options.resumeCompletionContract,
)
: rotatedRunAttachPayload(
core.store.state,
desired,
this.#authorizedTools,
this.options.resumeCompletionContract,
);
this.#runAttachTemplate = structuredClone(runAttachTemplate);
const payload = {
...runAttachTemplate,
paperclipNextAuthority: { identity: desired, connection },
};
core.queueCommand("run.attach", payload, commandId, true);
await this.#waitCommand("run.attach", commandId);
const attached = core.getCommand(commandId);
if (attached?.status !== "completed") {
throw new Error("native_runner_prp_run_rotation_failed");
}
activationStarted = true;
core.rotateRunIdentity(desired, runAttachTemplate);
await registration?.activate?.();
if (registration?.failure) {
void registration.failure.catch((error: unknown) => {
this.#failTransport(
error instanceof Error ? error : new Error(String(error)),
);
});
}
await this.#awaitRegistrationReady(registration?.ready);
const activationDeadline =
Date.now() + (this.options.runnerReconnectGraceMs ?? 5_000);
while (
!recoveryIdentityMatches(core.store.state.identity, desired) ||
core.store.state.warmTransition !== undefined ||View on GitHub (pinned to 01ad858492)
Solutions
- Inspect runner logs for why the run.attach command failed
- Verify runId/turnId/itemId are valid and match an existing run on the runner
- Ensure transport and runner versions agree on the run.attach protocol
- Retry the attach after fixing the runner-side cause
Example fix
// before
await transport.attachRun({ runId: staleRunId, turnId, itemId });
// after
const run = await resolveRun(staleRunId);
if (!run) throw new Error(`run ${staleRunId} not found on runner`);
await transport.attachRun({ runId: run.id, turnId, itemId }); Defensive patterns
Strategy: try-catch
Validate before calling
if (!runId || !turnId || !itemId) throw new Error('attachRun requires runId, turnId, itemId'); Try / catch
try {
await transport.attachRun({ runId, turnId, itemId });
} catch (e) {
if (e.message === 'native_runner_prp_run_rotation_failed') {
const detail = await fetchRunnerAttachFailure(runId);
logger.error('run.attach rejected by runner', detail);
throw new Error(`run rotation failed: ${detail}`);
} else throw e;
} Prevention
- Validate runId/turnId/itemId against the runner's known state before attaching
- Keep transport and runner versions in lockstep
- Log command IDs so runner-side attach failures can be correlated
When it happens
Trigger: core.getCommand(commandId).status is 'failed' or otherwise non-completed after #waitCommand resolves for the 'run.attach' payload — e.g. the runner rejected the desired DurableRecoveryIdentity or the attach handler errored.
Common situations: Runner-side attach failure due to invalid runId/turnId/itemId; runner state incompatible with the desired identity; protocol mismatch between transport and runner versions.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- native_runner_warm_attachment_not_quiescent
- native_runner_authority_unavailable
- native_runner_warm_attachment_ambiguous: expected one authen
- native_runner_warm_attachment_runner_exited: runner exited b
- provider_transport_failed: warm runner did not re-authentica
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/d286776f4635023c.
Report an issue: GitHub.