can1357/oh-my-pi · error

${label} must be an array of strings

Error message

${label} must be an array of strings

What it means

Thrown by stringArray() when a field expected to be an array of strings is not an array at all. Note it only raises this exact message for non-arrays; if the value is an array but an element is not a string, the nested rawString error fires instead ('<label> item must be a string'). Used for fields like command argument lists in daemon specs, wire requests, and RPC results.

Source

Thrown at packages/coding-agent/src/launch/protocol.ts:192

}

function booleanValue(value: unknown, label: string): boolean {
	if (typeof value !== "boolean") throw new Error(`${label} must be a boolean`);
	return value;
}

function numberValue(value: unknown, label: string): number {
	if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${label} must be a finite number`);
	return value;
}

function optionalNumber(value: unknown, label: string): number | undefined {
	if (value === undefined) return undefined;
	return numberValue(value, label);
}

function stringArray(value: unknown, label: string): string[] {
	if (!Array.isArray(value)) throw new Error(`${label} must be an array of strings`);
	const result: string[] = [];
	for (const item of value) result.push(rawString(item, `${label} item`));
	return result;
}

function stringRecord(value: unknown, label: string): Record<string, string> {
	const source = record(value, label);
	const result: Record<string, string> = {};
	for (const key in source) result[key] = rawString(source[key], `${label}.${key}`);
	return result;
}

function daemonState(value: unknown): DaemonState {
	const state = stringValue(value, "daemon state");
	if (state === "starting" || state === "running" || state === "ready" || state === "restarting") return state;
	if (state === "stopping" || state === "exited" || state === "failed") return state;
	throw new Error(`Unknown daemon state: ${state}`);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the field is a JSON array: split a single command string with a shell-word splitter or manually
  2. Fix the producer to never serialize null/undefined for this field — use an empty array
  3. Check each element is a string to avoid the nested 'item must be a string' error

Example fix

// before
parseDaemonSpec({ id: "d1", command: "sleep 1" })
// after
parseDaemonSpec({ id: "d1", command: ["sleep", "1"] })
Defensive patterns

Strategy: validation

Validate before calling

function isStringArray(v: unknown): v is string[] {
  return Array.isArray(v) && v.every((x) => typeof x === "string");
}
if (!isStringArray(payload.command)) throw new Error("command must be a string[]");

Type guard

function isStringArray(v: unknown): v is string[] { return Array.isArray(v) && v.every((x) => typeof x === "string"); }

Try / catch

try {
  const spec = parseDaemonSpec(payload);
} catch (err) {
  if (err instanceof Error && err.message.includes("must be an array of strings")) {
    // split a shell-string command or default to [] before retrying
  } else throw err;
}

Prevention

When it happens

Trigger: parseDaemonSpec/parseDaemonWireRequest/parseDaemonRpcResult receiving e.g. command: "sleep 1" (a string, not an array), command: null, command: undefined, or command: {0:"a"}.

Common situations: Shell-string commands pasted into config without splitting into argv arrays; a producer serializing null after failed arg collection; YAML/JSON5 configs where quoting collapsed a list into a string.

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/61eb30adad48e2d4. Report an issue: GitHub.