paperclipai/paperclip · error

native_runner_prp_run_rotation_unavailable

Error message

native_runner_prp_run_rotation_unavailable

What it means

attachRun requires both a live runner core and a completed startup sequence. If either is missing (core null or #startupComplete false), the run-rotation machinery is unavailable and this error is thrown.

Source

Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:3612

        this.#failureSignal,
      ]);
    }
    throw new Error(
      `provider_transport_failed: warm runner did not re-authenticate within ${this.options.runnerReconnectGraceMs ?? 5_000}ms`,
    );
  }

  async attachRun(input: {
    runId: string;
    turnId: string;
    itemId: string;
  }): Promise<void> {
    if (this.#pendingWarmRecoveryCompletion !== null) {
      throw new Error("native_runner_warm_transition_completion_pending");
    }
    const core = this.#core;
    if (!core || !this.#startupComplete) {
      throw new Error("native_runner_prp_run_rotation_unavailable");
    }
    await this.#awaitWarmRunAttachmentReady();
    const prior = core.store.state.identity;
    const desired: DurableRecoveryIdentity = {
      ...prior,
      runId: input.runId,
      turnId: input.turnId,
      itemId: input.itemId,
    };
    const registration = this.options.controlPlaneRegistration
      ? await this.options.controlPlaneRegistration(core, desired)
      : null;
    const previousRelease = this.#controlPlaneRelease;
    let previousReleased = false;
    let activationStarted = false;
    try {
      const connection: RunnerProcessConnection =
        registration?.connection ??

View on GitHub (pinned to 01ad858492)

Solutions

  1. Await the transport's startup/ready promise before calling attachRun
  2. Check startup diagnostics to find why startup never completed
  3. Restart the transport if the core is gone
  4. Add a readiness check in calling code before attach

Example fix

// before
const transport = new RunnerdCodexTransport(...);
await transport.attachRun(input);
// after
const transport = new RunnerdCodexTransport(...);
await transport.whenReady();
await transport.attachRun(input);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!transport.isReady?.()) throw new Error('transport not ready: start transport before attachRun');

Type guard

function isTransportReady(t) {
  return typeof t.isReady === 'function' && t.isReady() === true;
}

Try / catch

try {
  await transport.attachRun(input);
} catch (e) {
  if (e.message === 'native_runner_prp_run_rotation_unavailable') {
    await transport.whenReady();
    await transport.attachRun(input);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling attachRun before the transport finished its startup handshake, or after the core was torn down/failure left it unusable.

Common situations: Calling attachRun immediately after constructing the transport without awaiting startup; startup failed earlier (e.g. runner never came up); attach attempted after stop().

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@01ad858492 (2026-09-10). Data as JSON: /api/errors/8afb8011b0d35269. Report an issue: GitHub.