{"record":{"id":"c7bdad7d3b532759","repo":"paperclipai/paperclip","slug":"runner-prp-command-missing","errorCode":"runner_prp_command_missing","errorMessage":"runner_prp_command_missing:${commandId}","messagePattern":"runner_prp_command_missing:(.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/runner-prp-coordinator.ts","lineNumber":403,"sourceCode":"              terminalEvent,\n              new Promise<never>((_resolve, reject) => {\n                timer = setTimeout(\n                  () => reject(new Error(\"runner_prp_terminal_timeout\")),\n                  timeoutMs,\n                );\n                timer.unref();\n              }),\n            ]);\n          } finally {\n            if (timer) clearTimeout(timer);\n          }\n        },\n        waitForCommand: async (commandId, timeoutMs = 30_000) => {\n          if (released) throw new Error(\"runner_prp_session_released\");\n          const deadline = Date.now() + timeoutMs;\n          while (Date.now() < deadline) {\n            const outcome = authority.commandOutcome(commandId);\n            if (!outcome) throw new Error(`runner_prp_command_missing:${commandId}`);\n            if (outcome.status === \"completed\") return outcome.result;\n            if (outcome.status === \"failed\" || outcome.status === \"rejected\") {\n              const message = outcome.result && typeof outcome.result.message === \"string\"\n                ? outcome.result.message\n                : `runner_prp_command_${outcome.status}:${commandId}`;\n              throw new Error(message);\n            }\n            await new Promise<void>((resolve) => {\n              const timer = setTimeout(resolve, 10);\n              timer.unref();\n            });\n          }\n          throw new Error(`runner_prp_command_timeout:${commandId}`);\n        },\n        waitForGoalEvent: async (requestId, timeoutMs = 30_000) => {\n          if (released) throw new Error(\"runner_prp_session_released\");\n          if (observedGoalRequests.has(requestId)) {\n            const error = observedGoalRequests.get(requestId);","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/native-runtime/runner-prp-coordinator.ts#L385-L421","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nconst { commandId } = session.queueCommand('question', payload, myOwnId);\nawait session.waitForCommand(myOwnId); // wrong id source\n\n// after\nconst { commandId } = session.queueCommand('question', payload);\nawait session.waitForCommand(commandId);","handlingStrategy":"validation","validationCode":"// Wait only on ids obtained from this session's queueCommand:\nconst { commandId } = session.queueCommand(type, payload);\n// optional assertion before waiting:\nif (typeof commandId !== 'string' || !commandId) throw new Error('commandId required from queueCommand');","typeGuard":null,"tryCatchPattern":"try {\n  return await session.waitForCommand(commandId, 30_000);\n} catch (e) {\n  const m = /^runner_prp_command_missing:(.+)$/.exec(e.message);\n  if (m) {\n    // unknown id: re-queue instead of retrying the wait\n    const cmd = session.queueCommand(type, payload);\n    return session.waitForCommand(cmd.commandId, 30_000);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["command-dispatch","native-runtime","state-loss"],"backgroundTag":"record-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}