paperclipai/paperclip · error · Error
runner_prp_command_${outcome.status}:${command.commandId}
Error message
runner_prp_command_${outcome.status}:${command.commandId} What it means
When the authority reports a queued PRP command as failed or rejected, queueLiveRunnerPrpCommand throws with the outcome's message if it is a string, otherwise this synthesized 'runner_prp_command_<status>:<commandId>' message. It surfaces the remote command outcome to the steering caller.
Source
Thrown at server/src/realtime/runner-prp-ws.ts:216
);
return {
runId,
commandId: command.commandId,
controllerSeq: command.controllerSeq,
completion: (async () => {
const deadline = Date.now() + 30_000;
while (Date.now() < deadline) {
const outcome = binding.authority.commandOutcome(command.commandId);
if (!outcome) {
throw new Error(`runner_prp_command_missing:${command.commandId}`);
}
if (outcome.status === "completed") return outcome.result;
if (outcome.status === "failed" || outcome.status === "rejected") {
const message =
outcome.result && typeof outcome.result.message === "string"
? outcome.result.message
: `runner_prp_command_${outcome.status}:${command.commandId}`;
throw new Error(message);
}
await new Promise<void>((resolve) => {
const timer = setTimeout(resolve, 10);
timer.unref();
});
}
throw new Error(`runner_prp_command_timeout:${command.commandId}`);
})(),
};
}
export class RunnerPrpRuntimeRequestResolutionError extends Error {
constructor(
readonly code:
| "runner_prp_authority_not_active"
| "runtime_request_resolution_conflict",
) {
super(code);View on GitHub (pinned to 01ad858492)
Solutions
- Read the status embedded in the message (failed vs rejected) to decide retry vs abort
- Inspect runner logs for the commandId to find the underlying failure
- Resend the command if the failure was transient; do not resend if rejected due to run state
- Have the runner return a string result.message so callers get actionable errors
Example fix
// before
await steer(runId, text); // throws runner_prp_command_failed:<id>
// after
try {
await steer(runId, text);
} catch (e) {
if (String(e.message).startsWith('runner_prp_command_rejected')) {
// run state rejected it; do not blind-retry
} else if (String(e.message).startsWith('runner_prp_command_failed')) {
await steer(runId, text); // transient; retry
} else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// check run turn state before steering const state = await getRunState(runId); if (state.busy) await waitForIdle(runId);
Type guard
const hasMessage = (r) => !!r && typeof r.message === 'string';
Try / catch
try { await steer(runId, text) } catch (e) { if (/runner_prp_command_rejected/.test(e.message)) abort(); else if (/runner_prp_command_failed/.test(e.message)) retryOnce(); else throw e; } Prevention
- Have runners always return a string result.message for failed/rejected outcomes
- Check run/turn state before sending steer or native-control commands
- Distinguish rejected (state conflict, do not retry) from failed (transient, retry)
- Correlate commandId with runner logs when diagnosing outcomes
When it happens
Trigger: steer/nativeControl command is delivered but the runner authority completes it with status failed or rejected and no string message in outcome.result.
Common situations: Runner rejected a steer because the turn was busy or in an invalid state; native control command failed on the runner; runner returned a non-string error payload.
Related errors
- runner_prp_command_missing:${command.commandId}
- [opencode-local] Remote model availability probe for "${mode
- The duplex channel launch command needs at least one argumen
- [paperclip] shapePaperclipWorkspaceEnvForExecution called wi
- Unsupported test-drive harness: ${String(harness)}.
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/ec072bb1c119ded1.
Report an issue: GitHub.