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
- Ensure the transport's start/activation sequence completed successfully before issuing PRP commands.
- Check whether an earlier error or shutdown nulled `#core`; re-create/restart the transport instead of reusing it.
- Guard callers so commands are not enqueued concurrently with teardown.
- 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
- Always await transport.start() before any command API.
- Serialize commands against shutdown with a lifecycle lock.
- Treat a failed start as terminal: recreate the transport instead of reusing it.
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
- Attempt journal is closed
- Cloud runtime identity provider is not initialized
- provider turn ended with status ${turn.status}
- Plugin UI is not available (status: ${plugin.status})
- Plugin is not ready (current status: ${plugin.status})
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/e1e48e6b6fddc3e7.
Report an issue: GitHub.