paperclipai/paperclip · error
runner_prp_command_missing
runner_prp_command_missing
Error message
runner_prp_command_missing:${commandId} What it means
Inside waitForCommand, the coordinator polls authority.commandOutcome(commandId) every 10ms. If the authority has no record of the given commandId at all, it throws 'runner_prp_command_missing:<id>' immediately rather than waiting for the timeout. This means the command was never queued through this session's authority, or the outcome record was discarded (e.g. after a disconnect/rebind that cleared pending command state).
Source
Thrown at server/src/services/native-runtime/runner-prp-coordinator.ts:403
terminalEvent,
new Promise<never>((_resolve, reject) => {
timer = setTimeout(
() => reject(new Error("runner_prp_terminal_timeout")),
timeoutMs,
);
timer.unref();
}),
]);
} finally {
if (timer) clearTimeout(timer);
}
},
waitForCommand: async (commandId, timeoutMs = 30_000) => {
if (released) throw new Error("runner_prp_session_released");
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const outcome = authority.commandOutcome(commandId);
if (!outcome) throw new Error(`runner_prp_command_missing:${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}:${commandId}`;
throw new Error(message);
}
await new Promise<void>((resolve) => {
const timer = setTimeout(resolve, 10);
timer.unref();
});
}
throw new Error(`runner_prp_command_timeout:${commandId}`);
},
waitForGoalEvent: async (requestId, timeoutMs = 30_000) => {
if (released) throw new Error("runner_prp_session_released");
if (observedGoalRequests.has(requestId)) {
const error = observedGoalRequests.get(requestId);View on GitHub (pinned to 01ad858492)
Solutions
- Confirm the commandId came from the return value of session.queueCommand on the SAME session instance before waiting on it.
- Log/inspect authority.commandOutcome(commandId) (or the command registry) to see whether the id exists at all; if the state was reset, re-queue the command.
- Catch /runner_prp_command_missing:(.+)/, extract the id, and treat it as a permanent failure — do not retry the wait, re-issue the command instead.
- Align id generation: ensure caller and coordinator share the same id namespace (queueCommand's optional commandId parameter must match what waitForCommand receives).
Example fix
// before
const { commandId } = session.queueCommand('question', payload, myOwnId);
await session.waitForCommand(myOwnId); // wrong id source
// after
const { commandId } = session.queueCommand('question', payload);
await session.waitForCommand(commandId); Defensive patterns
Strategy: validation
Validate before calling
// Wait only on ids obtained from this session's queueCommand:
const { commandId } = session.queueCommand(type, payload);
// optional assertion before waiting:
if (typeof commandId !== 'string' || !commandId) throw new Error('commandId required from queueCommand'); Try / catch
try {
return await session.waitForCommand(commandId, 30_000);
} catch (e) {
const m = /^runner_prp_command_missing:(.+)$/.exec(e.message);
if (m) {
// unknown id: re-queue instead of retrying the wait
const cmd = session.queueCommand(type, payload);
return session.waitForCommand(cmd.commandId, 30_000);
}
throw e;
} Prevention
- Always derive waitForCommand ids from the queueCommand return value on the same session instance.
- Never persist commandIds across process restarts and expect them to resolve against a fresh authority.
- Keep id generation in one place; avoid hand-rolling commandId strings.
- After a runner reconnect, re-queue pending commands rather than re-waiting on old ids.
When it happens
Trigger: Calling waitForCommand with a commandId that was never returned by queueCommand on this session; a typo'd or truncated id; calling waitForCommand for a command queued on a previous session whose authority state was reset after reconnect; passing a controller-generated id that was never registered with the authority.
Common situations: Persisting commandIds in a DB and replaying waitForCommand after a server restart; mismatched id formats between caller and authority (e.g. prefix added/removed across versions); assuming waitForCommand waits for any command instead of the specific pre-registered one.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- ${outcome.result.message} (fallback: runner_prp_command_${ou
- runner_prp_command_timeout
- Paperclip run authentication is unavailable
- paperclip_runner_approval_not_found
- paperclip_runner_codex_permission_mode_unqualified
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/c7bdad7d3b532759.
Report an issue: GitHub.