can1357/oh-my-pi · error
${label} must be a string
Error message
${label} must be a string What it means
This error is thrown by rawString(), the strict string validator in the daemon launch protocol parser. It fires whenever a field decoded from daemon wire JSON (spec, request, snapshot, or RPC result) is not a string. It exists to fail fast on malformed IPC payloads instead of letting non-string values propagate into daemon state.
Source
Thrown at packages/coding-agent/src/launch/protocol.ts:162
}
export type DaemonWireMessage = DaemonWireResponse | DaemonCompletionNotification;
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function record(value: unknown, label: string): Record<string, unknown> {
if (!isRecord(value)) throw new Error(`${label} must be an object`);
return value;
}
function stringValue(value: unknown, label: string): string {
if (typeof value !== "string" || value.length === 0) throw new Error(`${label} must be a non-empty string`);
return value;
}
function rawString(value: unknown, label: string): string {
if (typeof value !== "string") throw new Error(`${label} must be a string`);
return value;
}
function optionalString(value: unknown, label: string): string | undefined {
if (value === undefined) return undefined;
return stringValue(value, label);
}
function optionalRawString(value: unknown, label: string): string | undefined {
if (value === undefined) return undefined;
return rawString(value, label);
}
function booleanValue(value: unknown, label: string): boolean {
if (typeof value !== "boolean") throw new Error(`${label} must be a boolean`);
return value;
}
View on GitHub (pinned to 9690622007)
Solutions
- Inspect the label in the message to find which field is not a string
- Fix the producer of the payload to emit a non-empty string for that field (empty strings are allowed here, only non-strings rejected — for empty rejection use stringValue)
- If the field is genuinely optional, have the producer omit it and parse with optionalRawString instead
- Check daemon/client version mismatch and align both sides on the same wire schema
Example fix
// before
parseDaemonSpec({ id: 42, command: ["sleep"] })
// after
parseDaemonSpec({ id: "42", command: ["sleep"] }) Defensive patterns
Strategy: validation
Validate before calling
function isPlainObject(v: unknown): v is Record<string, unknown> { return typeof v === "object" && v !== null; }
function assertStringFields(obj: unknown, fields: string[]): void {
if (!isPlainObject(obj)) throw new Error("payload must be an object");
for (const f of fields) {
const v = obj[f];
if (typeof v !== "string") throw new Error(`field '${f}' must be a string, got ${typeof v}`);
}
}
// before calling: assertStringFields(payload, ["id", "commandLabel"]); Type guard
function isString(v: unknown): v is string { return typeof v === "string"; } Try / catch
try {
const spec = parseDaemonSpec(payload);
} catch (err) {
if (err instanceof Error && err.message.includes("must be a string")) {
// log payload field types and reject/repair the payload
} else throw err;
} Prevention
- Validate wire payloads against a JSON schema at the producer, not just the consumer
- Never serialize undefined/null for required string fields — omit or default them
- Add a shared type/interface between producer and consumer and typecheck both sides
- Version the wire protocol and gate parsing on a protocolVersion field
When it happens
Trigger: Calling parseDaemonSpec, parseDaemonWireRequest, parseDaemonOperation, or parseDaemonRpcResult with a payload where a required field (e.g. spec id, command args item, rpc result field) is undefined, null, a number, or an object instead of a string. Also thrown indirectly by stringArray/stringRecord when an array item or record value is not a string (message becomes '<label> item must be a string').
Common situations: Hand-written daemon config JSON with a missing or mistyped field; an older daemon binary writing a wire format that changed; a client sending a numeric PID or port where a string id is expected; JSON round-tripping that turned a string into a nested object.
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
- ${label} must be a boolean
- ${label} must be a finite number
- ${label} must be an array of strings
- Unknown daemon state: ${state}
- daemon.readyPending must be an array
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/336f08d1aedd3133.
Report an issue: GitHub.