can1357/oh-my-pi · error
result.daemons must be an array
Error message
result.daemons must be an array
What it means
parseDaemonRpcResult validates the `list` operation's result: `result.daemons` must be an array before each element is decoded into a daemon snapshot. A non-array means the broker returned a malformed or unexpected payload for the list call.
Source
Thrown at packages/coding-agent/src/launch/protocol.ts:415
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) };
}
case "logs":
return {
op: "logs",
name: stringValue(source.name, "result.name"),
text: typeof source.text === "string" ? source.text : "",
terminalRows:
source.terminalRows === undefined ? undefined : stringArray(source.terminalRows, "result.terminalRows"),
terminalText:
source.terminalText === undefined ? undefined : rawString(source.terminalText, "result.terminalText"),
cursor: numberValue(source.cursor, "result.cursor"),
timedOut: booleanValue(source.timedOut, "result.timedOut"),
state: daemonState(source.state),
};
case "wait":
return {
op: "wait",View on GitHub (pinned to 9690622007)
Solutions
- Restart the daemon so its result shape matches the client's parser.
- Inspect the raw result payload to see what `daemons` actually contains.
- Update client and daemon to the same version.
- If writing a mock/test broker, return `{ op: "list", daemons: [...] }` with daemons as an array of snapshot objects.
Example fix
// before (mock broker)
res({ op: "list", daemons: null });
// after
res({ op: "list", daemons: [snapshot] }); Defensive patterns
Strategy: validation
Validate before calling
function isListResult(v: unknown): v is { op: "list"; daemons: unknown[] } {
return typeof v === "object" && v !== null && Array.isArray((v as any).daemons);
} Type guard
const hasDaemonsArray = (v: unknown): v is { daemons: unknown[] } =>
typeof v === "object" && v !== null && Array.isArray((v as { daemons?: unknown }).daemons); Try / catch
try {
const result = await broker.list();
} catch (err) {
if (err.message.includes("result.daemons must be an array")) {
logger.warn("broker returned malformed list result");
return [];
}
throw err;
} Prevention
- Version-gate protocol result shapes
- Test mock brokers against the real parser
- Log raw results on parse failure
- Keep daemon and client builds in lockstep
When it happens
Trigger: Calling the list operation against a daemon whose response lacks `daemons`, sets it to null/an object, or whose protocol version shapes the result differently.
Common situations: Daemon/client version skew, a broker bug after an internal refactor, or intercepting/transforming socket traffic (proxy, logging shim) that mangles the envelope.
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
- operation.for must be ready or exit
- Unknown daemon operation: ${op}
- ${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/1681bc0c5d19fb4e.
Report an issue: GitHub.