paperclipai/paperclip · error · Error
runner_prp_command_missing:${command.commandId}
Error message
runner_prp_command_missing:${command.commandId} What it means
Thrown by queueLiveRunnerPrpCommand when, after the command is queued on the live binding, the authority's commandOutcome lookup returns nothing for the commandId within the polling loop — i.e. the command was never registered or was dropped before producing an outcome. The message carries the missing commandId.
Source
Thrown at server/src/realtime/runner-prp-ws.ts:208
const binding = registrations.get(current.runId);
if (!binding || binding.generation !== current.generation) return null;
const runId = current.runId;
const command = binding.authority.queueCommand(
input.type,
input.payload ?? {},
input.commandId,
true,
);
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}`);
})(),
};
}View on GitHub (pinned to 01ad858492)
Solutions
- Verify the command was accepted/registered by the authority before polling its outcome
- Re-acquire the current authority binding and resend the command with a new commandId
- Handle runner reconnects: invalidate in-flight commands on re-registration
- Check for race between queueLiveRunnerPrpCommand and authority re-registration
Example fix
// before
await queueLiveRunnerPrpCommand(binding, command); // throws runner_prp_command_missing
// after
try {
await queueLiveRunnerPrpCommand(binding, command);
} catch (e) {
if (String(e.message).startsWith('runner_prp_command_missing')) {
const fresh = getLiveBinding(command.runId);
return queueLiveRunnerPrpCommand(fresh, { ...command, commandId: newId() });
}
throw e;
} Defensive patterns
Strategy: retry
Validate before calling
if (!getLiveBinding(command.runId) || binding.generation !== currentGeneration) await reacquireBinding();
Try / catch
try { await queueLiveRunnerPrpCommand(binding, cmd) } catch (e) { if (String(e.message).startsWith('runner_prp_command_missing')) { return resendOnFreshBinding(cmd); } throw e; } Prevention
- Invalidate in-flight commands when an authority re-registers
- Confirm command registration before entering the outcome polling loop
- Re-fetch the current binding instead of caching one across reconnects
- Generate fresh commandIds on resend to avoid stale outcome lookups
When it happens
Trigger: Calling steer/nativeControl on a live runner binding whose authority lost or never recorded the command (e.g. authority re-registered between queueing and outcome lookup, command id mismatch).
Common situations: Runner reconnected mid-command so the old authority's outcome map is gone; bug dropping the command before registration; stale binding used after reconnect.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- runner_prp_command_${outcome.status}:${command.commandId}
- The duplex channel launch command needs at least one argumen
- Unsupported test-drive harness: ${String(harness)}.
- Durable PRP control plane is not listening.
- Capability live session is not connected
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/8b9e34bc5f3359d1.
Report an issue: GitHub.