{"record":{"id":"ceb15e186f191b84","repo":"can1357/oh-my-pi","slug":"pending-daemon-completion-is-not-an-event","errorCode":null,"errorMessage":"Pending daemon completion is not an event","messagePattern":"Pending daemon completion is not an event","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/broker.ts","lineNumber":1297,"sourceCode":"\t\t\t\t\tgeneration: 0,\n\t\t\t\t\tstopRequested: !detached || snapshot.state === \"stopping\",\n\t\t\t\t\tlogReady: detached && (!spec.ready?.log || snapshot.state === \"ready\"),\n\t\t\t\t\tportReady: detached && (spec.ready?.port === undefined || snapshot.state === \"ready\"),\n\t\t\t\t\treadinessBuffer: \"\",\n\t\t\t\t\toutputOffset: detached ? snapshot.outputBytes : 0,\n\t\t\t\t\treadyPattern: spec.ready?.log ? new RegExp(spec.ready.log, \"u\") : undefined,\n\t\t\t\t\tconsecutiveFailures: 0,\n\t\t\t\t\tpersistQueue: Promise.resolve(),\n\t\t\t\t\tcompletionCapable: \"completionEvents\" in decoded && decoded.completionEvents === true,\n\t\t\t\t\tcompletionSubscriptionId:\n\t\t\t\t\t\t\"completionSubscriptionId\" in decoded && typeof decoded.completionSubscriptionId === \"string\"\n\t\t\t\t\t\t\t? decoded.completionSubscriptionId\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tpendingCompletions: (() => {\n\t\t\t\t\t\tif (\"pendingCompletions\" in decoded && Array.isArray(decoded.pendingCompletions)) {\n\t\t\t\t\t\t\treturn decoded.pendingCompletions.map(value => {\n\t\t\t\t\t\t\t\tconst message = parseDaemonWireMessage(value);\n\t\t\t\t\t\t\t\tif (!(\"event\" in message)) throw new Error(\"Pending daemon completion is not an event\");\n\t\t\t\t\t\t\t\treturn message;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst pendingSnapshot =\n\t\t\t\t\t\t\t\"pendingCompletion\" in decoded\n\t\t\t\t\t\t\t\t? parseDaemonSnapshot(decoded.pendingCompletion)\n\t\t\t\t\t\t\t\t: \"completionPending\" in decoded && decoded.completionPending === true\n\t\t\t\t\t\t\t\t\t? { ...snapshot }\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\treturn pendingSnapshot\n\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tevent: \"daemon-completed\",\n\t\t\t\t\t\t\t\t\t\tcompletionId: crypto.randomUUID(),\n\t\t\t\t\t\t\t\t\t\towner: pendingSnapshot.owner ?? snapshot.owner ?? \"\",\n\t\t\t\t\t\t\t\t\t\tdaemon: pendingSnapshot,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t]","sourceCodeStart":1279,"sourceCodeEnd":1315,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/broker.ts#L1279-L1315","documentation":"When the broker decodes a pendingCompletions snapshot from the wire, each entry must be a daemon *event* message. parseDaemonWireMessage may return other message kinds (e.g. results or requests); if a pending completion entry parses to a message without an 'event' field, the broker throws this error to fail fast on malformed wire data.","triggerScenarios":"A daemon wire message inside the pendingCompletions array of a broker handshake/snapshot payload lacks an 'event' discriminator — i.e. a non-event message was queued or serialized as a pending completion.","commonSituations":"Version mismatch between broker and client where the pendingCompletions serialization changed; corrupted or hand-edited runtime state; a bug in code that enqueues results into pendingCompletions.","solutions":["Upgrade both broker and client to matching versions so pendingCompletions always carries event messages","Delete stale runtime state under the daemon runtime dir and restart the broker","Inspect the serialized pendingCompletions payload to find the non-event entry and fix the producer"],"exampleFix":"// before\nconst message = parseDaemonWireMessage(value); // may be any kind\nreturn message;\n// after\nconst message = parseDaemonWireMessage(value);\nif (!(\"event\" in message)) throw new Error(\"Pending daemon completion is not an event\");\nreturn message;","handlingStrategy":"validation","validationCode":"if (!Array.isArray(decoded.pendingCompletions)) throw new Error('pendingCompletions must be an array');\nfor (const value of decoded.pendingCompletions) {\n  const msg = parseDaemonWireMessage(value);\n  if (!(\"event\" in msg)) throw new Error('non-event pending completion: ' + JSON.stringify(value));\n}","typeGuard":"function isDaemonEventMessage(m: ReturnType<typeof parseDaemonWireMessage>): m is DaemonEventMessage {\n  return \"event\" in m;\n}","tryCatchPattern":"try {\n  const completions = decoded.pendingCompletions.map(v => parseDaemonWireMessage(v));\n} catch (err) {\n  logger.warn('Malformed pendingCompletions, dropping snapshot', { error: err.message });\n}","preventionTips":["Only enqueue messages with an 'event' discriminator into pendingCompletions","Validate payloads with parseDaemonWireMessage at the producer, not just the consumer","Keep broker and client protocol versions in lockstep"],"tags":["ipc","wire-protocol","validation"],"backgroundTag":"wire-protocol-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}