paperclipai/paperclip · error
PRP command ${type} ${command.status}: ${JSON.stringify(comm
Error message
PRP command ${type} ${command.status}: ${JSON.stringify(command.result)} What it means
Thrown from `#waitCommand` when the awaited PRP command record exists but its status is neither `pending` nor `completed` (e.g. `failed`, `cancelled`, `timeout`). The message embeds the status and the JSON of `command.result`, so the durable failure payload is visible in the error.
Source
Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:5550
throw new Error("runnerd did not report its provider identity");
}
async #waitCommand(
type: string,
commandId?: string,
deadline = Date.now() + 30_000,
): Promise<void> {
while (Date.now() < deadline) {
this.#throwIfFailed();
const command =
commandId === undefined
? this.#core?.store.state.commands.find(
(candidate) => candidate.type === type,
)
: this.#core?.getCommand(commandId);
if (command?.status === "completed") return;
if (command !== undefined && command.status !== "pending") {
throw new Error(
`PRP command ${type} ${command.status}: ${JSON.stringify(command.result)}`,
);
}
if (await this.#runnerHasExited())
throw new Error(`runnerd exited while waiting for ${type}`);
await new Promise((resolveWait) => setTimeout(resolveWait, 10));
}
throw new Error(
`${this.#startupComplete ? "provider_transport_failed" : this.#startupFailureCode}: PRP command ${type} timed out`,
);
}
#pumpEvents(): void {
const core = this.#core;
// The controller may activate its new epoch before attachRun observes the
// confirmed handoff. Never consume either epoch with the other's cursor.
if (View on GitHub (pinned to 01ad858492)
Solutions
- Read the `command.result` JSON embedded in the message — it contains runnerd's reason for the failure; fix the underlying cause and retry.
- Retry the command with a fresh commandId.
- When a commandId is not supplied, always pass one so the lookup does not match an older failed command of the same type.
- Check runnerd logs for the command ID to see the full failure context; restart runnerd if the failure is internal.
Example fix
// before const command = this.#core?.store.state.commands.find((c) => c.type === type); // after const command = commandId ? this.#core?.getCommand(commandId) : this.#core?.store.state.commands.find((c) => c.type === type && c.status !== "failed");
Defensive patterns
Strategy: try-catch
Validate before calling
const cmd = core.getCommand(commandId);
if (cmd && cmd.status !== "pending" && cmd.status !== "completed") {
throw new Error(`command ${commandId} already terminal: ${cmd.status}`);
} Type guard
function isRetryableCommandFailure(err) {
return err instanceof Error && /^PRP command .* (failed|cancelled|timeout):/.test(err.message);
} Try / catch
try {
await waitCommand(type, commandId);
} catch (err) {
if (isRetryableCommandFailure(err)) {
log.error("PRP command failed", { result: err.message });
await requeueCommand(type, payload);
} else throw err;
} Prevention
- Always pass an explicit commandId to avoid matching unrelated failed commands.
- Validate command payloads against runnerd's schema before queueing.
- Log the embedded command.result JSON for every failure.
When it happens
Trigger: Waiting on a command (by explicit `commandId`, or by scanning `store.state.commands` for the first command of the given type when no id is supplied) and observing a terminal non-success status during the 10ms poll loop.
Common situations: runnerd rejected the command payload (validation failure recorded in `result`); the codex subprocess failed mid-command; command deadline exceeded inside runnerd; scanning by type when no id is passed picks up an unrelated failed command of the same type.
Related errors
- PRP command ${type} omitted its durable result
- The duplex channel launch command needs at least one argumen
- runnerd digest mismatch: expected ${request.runnerd.sha256},
- Unsupported test-drive harness: ${String(harness)}.
- provider_initialize_protocol_error
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/bb8b4b1ff605b006.
Report an issue: GitHub.