paperclipai/paperclip · error
PRP provider thread is already started
Error message
PRP provider thread is already started
What it means
Double-start guard on the runnerd Codex transport's #start: the provider thread is single-instance — #core must be null when starting — so a second start request while a provider thread is already running throws this sentinel instead of spawning a duplicate core. It fires on re-entrant or duplicate start RPCs (e.g. a start retried after a timeout that actually succeeded).
Solutions
- Check the start result/state before retrying a start that may have already succeeded (idempotent start handling).
- Stop the existing provider thread (close) before starting a new one if a restart is intended.
- Trace the duplicate start RPC to its source and deduplicate at the control-plane caller.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:4285 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/414af5ac6d0ae249.
Report an issue: GitHub.
Appendix: source
Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:4285
} finally {
await this.#core?.stop();
this.#controlPlaneCheckpoint = null;
this.#controlPlaneRelease = null;
}
if (suspensionRequired && !runnerSettled) {
throw new NativeSessionCloseUnrecoverableError();
}
if (this.#ownsRoot && !adoptedRunner) {
rmSync(this.#root, { recursive: true, force: true });
}
this.#publish();
}
async #start(
params: Record<string, unknown>,
): Promise<Record<string, unknown>> {
if (this.#core !== null)
throw new Error("PRP provider thread is already started");
if (this.options.adoptExistingRunner) {
// A crash can precede the first driver checkpoint even though runnerd
// already opened the provider. Exact process adoption must reuse that
// authority instead of enqueueing another run.prepare/session.open pair.
await this.#resume();
return this.#openedThreadResponse(params);
}
const token = randomUUID().replaceAll("-", "");
const identity = this.options.prpIdentity ?? {
runnerInstanceId: `runner_lab_${token}`,
environmentLeaseId: `lease_lab_${token}`,
runId: `run_lab_${token}`,
normalizedSessionId: `session_lab_${token}`,
turnId: `turn_lab_${token}`,
itemId: `item_lab_${token}`,
};
const runnerBinaryPath =
this.options.runnerBinary ?? defaultCapabilityRunnerdBinary();View on GitHub (pinned to 3f1d897a7c)