{"record":{"id":"1b3a26956a299d47","repo":"can1357/oh-my-pi","slug":"unknown-daemon-operation-op","errorCode":null,"errorMessage":"Unknown daemon operation: ${op}","messagePattern":"Unknown daemon operation: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/protocol.ts","lineNumber":398,"sourceCode":"\t\t}\n\t\tcase \"send\":\n\t\t\treturn {\n\t\t\t\top,\n\t\t\t\tname: stringValue(source.name, \"operation.name\"),\n\t\t\t\tdata: optionalString(source.data, \"operation.data\"),\n\t\t\t\tsignal: source.signal === undefined ? undefined : daemonSignal(source.signal),\n\t\t\t};\n\t\tcase \"stop\":\n\t\t\treturn {\n\t\t\t\top,\n\t\t\t\tname: stringValue(source.name, \"operation.name\"),\n\t\t\t\ttimeoutMs: numberValue(source.timeoutMs, \"operation.timeoutMs\"),\n\t\t\t};\n\t\tcase \"restart\":\n\t\tcase \"describe\":\n\t\t\treturn { op, name: stringValue(source.name, \"operation.name\") };\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown daemon operation: ${op}`);\n\t}\n}\n\n/** Decode a broker result using its pending operation as the discriminator. */\nexport function parseDaemonRpcResult(operation: DaemonOperation, value: unknown): DaemonRpcResult {\n\tconst source = record(value, `${operation.op} result`);\n\tswitch (operation.op) {\n\t\tcase \"ping\":\n\t\t\treturn { op: \"ping\", projectDir: stringValue(source.projectDir, \"result.projectDir\") };\n\t\tcase \"start\":\n\t\t\treturn {\n\t\t\t\top: \"start\",\n\t\t\t\tdaemon: parseDaemonSnapshot(source.daemon),\n\t\t\t\treadyTimedOut: booleanValue(source.readyTimedOut, \"result.readyTimedOut\"),\n\t\t\t};\n\t\tcase \"list\": {\n\t\t\tif (!Array.isArray(source.daemons)) throw new Error(\"result.daemons must be an array\");\n\t\t\treturn { op: \"list\", daemons: source.daemons.map(parseDaemonSnapshot) };","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/protocol.ts#L380-L416","documentation":"parseDaemonOperation dispatches on the operation name and throws for any op not in its switch table. This is the protocol's exhaustiveness guard: the broker only implements a fixed set of daemon operations.","triggerScenarios":"Sending a request whose `op` string is misspelled, unknown to this version, or invented (e.g. \"status\" when only start/stop/restart/list/logs/wait/describe exist).","commonSituations":"Client and daemon version mismatch (newer client sends an op the older daemon doesn't know), typos in hand-crafted RPC scripts, or third-party tooling speaking a divergent protocol dialect.","solutions":["Use a supported operation name; consult parseDaemonOperation in packages/coding-agent/src/launch/protocol.ts for the exact set.","Restart/upgrade the daemon so versions match the client.","Log the `op` value from the rejected request to spot typos.","If the op is genuinely new, add a case to parseDaemonOperation and a matching parseDaemonRpcResult branch."],"exampleFix":"// before\nawait broker.request({ op: \"status\", name: \"main\" });\n// after\nawait broker.request({ op: \"describe\", name: \"main\" });","handlingStrategy":"type-guard","validationCode":"const KNOWN_OPS = [\"start\", \"stop\", \"restart\", \"list\", \"logs\", \"wait\", \"describe\"] as const;\ntype Op = typeof KNOWN_OPS[number];\nfunction assertOp(op: string): asserts op is Op {\n  if (!KNOWN_OPS.includes(op as Op)) throw new Error(`unsupported daemon op: ${op}`);\n}","typeGuard":"const isKnownOp = (op: string): op is \"start\" | \"stop\" | \"restart\" | \"list\" | \"logs\" | \"wait\" | \"describe\" =>\n  [\"start\", \"stop\", \"restart\", \"list\", \"logs\", \"wait\", \"describe\"].includes(op);","tryCatchPattern":"try {\n  await broker.request(req);\n} catch (err) {\n  if (err.message.startsWith(\"Unknown daemon operation\")) {\n    logger.error(\"client/daemon protocol mismatch\", { op: req.op });\n  }\n  throw err;\n}","preventionTips":["Generate request types from the protocol module instead of hand-writing ops","Upgrade client and daemon together","Wrap raw socket RPC behind a typed API surface"],"tags":["protocol","validation","daemon","ipc"],"backgroundTag":"unsupported-operation","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}