can1357/oh-my-pi · error
Unknown daemon operation: ${op}
Error message
Unknown daemon operation: ${op} What it means
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.
Source
Thrown at packages/coding-agent/src/launch/protocol.ts:398
}
case "send":
return {
op,
name: stringValue(source.name, "operation.name"),
data: optionalString(source.data, "operation.data"),
signal: source.signal === undefined ? undefined : daemonSignal(source.signal),
};
case "stop":
return {
op,
name: stringValue(source.name, "operation.name"),
timeoutMs: numberValue(source.timeoutMs, "operation.timeoutMs"),
};
case "restart":
case "describe":
return { op, name: stringValue(source.name, "operation.name") };
default:
throw new Error(`Unknown daemon operation: ${op}`);
}
}
/** Decode a broker result using its pending operation as the discriminator. */
export function parseDaemonRpcResult(operation: DaemonOperation, value: unknown): DaemonRpcResult {
const source = record(value, `${operation.op} result`);
switch (operation.op) {
case "ping":
return { op: "ping", projectDir: stringValue(source.projectDir, "result.projectDir") };
case "start":
return {
op: "start",
daemon: parseDaemonSnapshot(source.daemon),
readyTimedOut: booleanValue(source.readyTimedOut, "result.readyTimedOut"),
};
case "list": {
if (!Array.isArray(source.daemons)) throw new Error("result.daemons must be an array");
return { op: "list", daemons: source.daemons.map(parseDaemonSnapshot) };View on GitHub (pinned to 9690622007)
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.
Example fix
// before
await broker.request({ op: "status", name: "main" });
// after
await broker.request({ op: "describe", name: "main" }); Defensive patterns
Strategy: type-guard
Validate before calling
const KNOWN_OPS = ["start", "stop", "restart", "list", "logs", "wait", "describe"] as const;
type Op = typeof KNOWN_OPS[number];
function assertOp(op: string): asserts op is Op {
if (!KNOWN_OPS.includes(op as Op)) throw new Error(`unsupported daemon op: ${op}`);
} Type guard
const isKnownOp = (op: string): op is "start" | "stop" | "restart" | "list" | "logs" | "wait" | "describe" => ["start", "stop", "restart", "list", "logs", "wait", "describe"].includes(op);
Try / catch
try {
await broker.request(req);
} catch (err) {
if (err.message.startsWith("Unknown daemon operation")) {
logger.error("client/daemon protocol mismatch", { op: req.op });
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- response.ok must be a boolean
- operation.for must be ready or exit
- result.daemons must be an array
- ${label} must be a string
- Unknown daemon state: ${state}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1b3a26956a299d47.
Report an issue: GitHub.