paperclipai/paperclip · error

PRP provider thread is not started

Error message

PRP provider thread is not started

What it means

Thrown from the private `#commandResult` helper of the runnerd Codex transport when the lazily-created provider-thread core (`this.#core`) is null. The helper can only queue and await durable PRP commands while the provider thread is started; without it there is nowhere to route `queueCommand`, so the call is rejected immediately.

Source

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

          durableRecoveryInternals.canonicalJson(payload)
      ) {
        throw new Error(
          `PRP steering correlation ${correlationId} was reused with different content`,
        );
      }
    } else {
      core.queueCommand(type, payload, commandId, true);
    }
    await this.#waitCommand(type, commandId);
  }

  async #commandResult(
    type: string,
    payload: Record<string, unknown>,
    deadline?: number,
  ): Promise<Record<string, unknown>> {
    const core = this.#core;
    if (core === null) throw new Error("PRP provider thread is not started");
    const commandId = `command_lab_${randomUUID().replaceAll("-", "")}`;
    core.queueCommand(type, payload, commandId, true);
    await this.#waitCommand(type, commandId, deadline);
    const command = core.getCommand(commandId);
    if (command?.status !== "completed" || command.type !== type) {
      throw new Error(`PRP command ${type} omitted its durable result`);
    }
    const result = record(record(command.result).result);
    const completion = this.#pendingWarmRecoveryCompletion;
    if (completion && type === "session.snapshot") {
      const expectation = this.#checkpointProviderIdentityExpectation;
      const providerIdentity = resolveRunnerdSessionIdentity(result);
      if (
        command.type !== "session.snapshot" ||
        core.store.state.warmTransition !== undefined ||
        !recoveryIdentityMatches(
          core.store.state.identity,
          completion.identity,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the transport's start/activation sequence completed successfully before issuing PRP commands.
  2. Check whether an earlier error or shutdown nulled `#core`; re-create/restart the transport instead of reusing it.
  3. Guard callers so commands are not enqueued concurrently with teardown.
  4. If using warm recovery, verify the checkpoint/provider-thread activation finished before sending session.snapshot commands.

Example fix

// before
await transport.#commandResult("session.snapshot", payload);
// after
if (!transport.isProviderThreadStarted()) {
  await transport.start();
}
await transport.#commandResult("session.snapshot", payload);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!transport.isProviderThreadStarted()) throw new Error("start the transport before issuing PRP commands");

Type guard

function canIssuePrpCommand(t) {
  return typeof t.isProviderThreadStarted === "function" && t.isProviderThreadStarted();
}

Try / catch

try {
  await issueCommand();
} catch (err) {
  if (err.message.includes("provider thread is not started")) {
    await transport.start();
    await issueCommand();
  } else throw err;
}

Prevention

When it happens

Trigger: Invoking any PRP command path (`#commandResult(type, payload, deadline)`) — e.g. session.snapshot or warm-recovery commands — before the transport's start/activation sequence has initialized the core, or after the core was torn down/reset to null during shutdown or failed recovery.

Common situations: Calling transport methods out of order (command issued before `start()` completed); a warm-recovery path running after the core was cleared; racing a shutdown against an in-flight command; a failed prior start leaving the transport half-initialized.

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/e1e48e6b6fddc3e7. Report an issue: GitHub.