can1357/oh-my-pi · error
operation.for must be ready or exit
Error message
operation.for must be ready or exit
What it means
parseDaemonOperation validates the `wait` operation's `for` field, which must be the literal string "ready" or "exit". Any other value is rejected because the broker can only wait on those two daemon lifecycle states.
Source
Thrown at packages/coding-agent/src/launch/protocol.ts:372
};
case "logs":
return {
op,
name: stringValue(source.name, "operation.name"),
lines: numberValue(source.lines, "operation.lines"),
head: booleanValue(source.head, "operation.head"),
grep: optionalString(source.grep, "operation.grep"),
follow: booleanValue(source.follow, "operation.follow"),
cursor: optionalNumber(source.cursor, "operation.cursor"),
renderTerminalRows:
source.renderTerminalRows === undefined
? undefined
: booleanValue(source.renderTerminalRows, "operation.renderTerminalRows"),
timeoutMs: numberValue(source.timeoutMs, "operation.timeoutMs"),
};
case "wait": {
const target = stringValue(source.for, "operation.for");
if (target !== "ready" && target !== "exit") throw new Error("operation.for must be ready or exit");
return {
op,
name: stringValue(source.name, "operation.name"),
for: target,
pattern: optionalString(source.pattern, "operation.pattern"),
timeoutMs: numberValue(source.timeoutMs, "operation.timeoutMs"),
};
}
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,View on GitHub (pinned to 9690622007)
Solutions
- Set `for` to exactly "ready" or "exit" in the request.
- Check protocol.ts for the current set of supported wait targets.
- Align client and daemon versions so both agree on operation fields.
- If a new wait target is needed, extend parseDaemonOperation in protocol.ts and regenerate/rebuild.
Example fix
// before
{ "op": "wait", "for": "started", "name": "main", "timeoutMs": 5000 }
// after
{ "op": "wait", "for": "ready", "name": "main", "timeoutMs": 5000 } Defensive patterns
Strategy: validation
Validate before calling
const VALID_WAIT_TARGETS = ["ready", "exit"] as const;
function buildWaitOp(forTarget: string) {
if (!VALID_WAIT_TARGETS.includes(forTarget as any)) throw new Error(`invalid wait target: ${forTarget}`);
return { op: "wait", for: forTarget };
} Type guard
const isValidWaitTarget = (v: unknown): v is "ready" | "exit" => v === "ready" || v === "exit";
Try / catch
try {
await broker.request({ op: "wait", for: target, name, timeoutMs });
} catch (err) {
if (err.message.includes("operation.for must be")) throw new Error(`bad wait target "${target}"; use ready|exit`);
throw err;
} Prevention
- Centralize request building so op fields come from one typed factory
- Use literal union types ("ready" | "exit") in client code
- Keep protocol docs in sync with protocol.ts
When it happens
Trigger: Sending a daemon wire request with op "wait" and `for` set to anything other than "ready"/"exit" — e.g. "started", "stopped", missing field, or a typo.
Common situations: Hand-written RPC payloads against the daemon socket, scripts copied from an older protocol version where other wait targets existed, or a client/daemon version skew.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- response.ok must be a boolean
- Unknown daemon operation: ${op}
- 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/fc1850cd2ff66c9e.
Report an issue: GitHub.