{"record":{"id":"61eb30adad48e2d4","repo":"can1357/oh-my-pi","slug":"label-must-be-an-array-of-strings","errorCode":null,"errorMessage":"${label} must be an array of strings","messagePattern":"(.+?) must be an array of strings","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/protocol.ts","lineNumber":192,"sourceCode":"}\n\nfunction booleanValue(value: unknown, label: string): boolean {\n\tif (typeof value !== \"boolean\") throw new Error(`${label} must be a boolean`);\n\treturn value;\n}\n\nfunction numberValue(value: unknown, label: string): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) throw new Error(`${label} must be a finite number`);\n\treturn value;\n}\n\nfunction optionalNumber(value: unknown, label: string): number | undefined {\n\tif (value === undefined) return undefined;\n\treturn numberValue(value, label);\n}\n\nfunction stringArray(value: unknown, label: string): string[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array of strings`);\n\tconst result: string[] = [];\n\tfor (const item of value) result.push(rawString(item, `${label} item`));\n\treturn result;\n}\n\nfunction stringRecord(value: unknown, label: string): Record<string, string> {\n\tconst source = record(value, label);\n\tconst result: Record<string, string> = {};\n\tfor (const key in source) result[key] = rawString(source[key], `${label}.${key}`);\n\treturn result;\n}\n\nfunction daemonState(value: unknown): DaemonState {\n\tconst state = stringValue(value, \"daemon state\");\n\tif (state === \"starting\" || state === \"running\" || state === \"ready\" || state === \"restarting\") return state;\n\tif (state === \"stopping\" || state === \"exited\" || state === \"failed\") return state;\n\tthrow new Error(`Unknown daemon state: ${state}`);\n}","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/protocol.ts#L174-L210","documentation":"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.","triggerScenarios":"parseDaemonSpec/parseDaemonWireRequest/parseDaemonRpcResult receiving e.g. command: \"sleep 1\" (a string, not an array), command: null, command: undefined, or command: {0:\"a\"}.","commonSituations":"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.","solutions":["Ensure the field is a JSON array: split a single command string with a shell-word splitter or manually","Fix the producer to never serialize null/undefined for this field — use an empty array","Check each element is a string to avoid the nested 'item must be a string' error"],"exampleFix":"// before\nparseDaemonSpec({ id: \"d1\", command: \"sleep 1\" })\n// after\nparseDaemonSpec({ id: \"d1\", command: [\"sleep\", \"1\"] })","handlingStrategy":"validation","validationCode":"function isStringArray(v: unknown): v is string[] {\n  return Array.isArray(v) && v.every((x) => typeof x === \"string\");\n}\nif (!isStringArray(payload.command)) throw new Error(\"command must be a string[]\");","typeGuard":"function isStringArray(v: unknown): v is string[] { return Array.isArray(v) && v.every((x) => typeof x === \"string\"); }","tryCatchPattern":"try {\n  const spec = parseDaemonSpec(payload);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"must be an array of strings\")) {\n    // split a shell-string command or default to [] before retrying\n  } else throw err;\n}","preventionTips":["Store commands as argv arrays in config, never single shell strings","Default missing list fields to [] rather than null at the producer","Use a shell-word splitter (e.g. string-argv) when accepting user command strings","Validate with zod: z.array(z.string()) before parsing"],"tags":["validation","ipc","arrays","type-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}