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

  1. Restart the daemon so its result shape matches the client's parser.
  2. Inspect the raw result payload to see what `daemons` actually contains.
  3. Update client and daemon to the same version.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/1681bc0c5d19fb4e. Report an issue: GitHub.