can1357/oh-my-pi · error
Unknown daemon state: ${state}
Error message
Unknown daemon state: ${state} What it means
Thrown by daemonState() when the 'state' field of a daemon snapshot or RPC result is a string but not one of the seven recognized states (starting, running, ready, restarting, stopping, exited, failed). The parser deliberately refuses unknown values so the state machine never handles an undefined state.
Source
Thrown at packages/coding-agent/src/launch/protocol.ts:209
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}`);
}
function restartPolicy(value: unknown): DaemonRestartPolicy {
const policy = stringValue(value, "restart policy");
if (policy === "no" || policy === "on-failure" || policy === "always") return policy;
throw new Error(`Unknown restart policy: ${policy}`);
}
function daemonSignal(value: unknown): DaemonSignal {
const signal = stringValue(value, "signal");
if (signal === "SIGINT" || signal === "SIGTERM" || signal === "SIGHUP") return signal;
if (signal === "SIGQUIT" || signal === "SIGKILL") return signal;
throw new Error(`Unknown daemon signal: ${signal}`);
}
function readyPendingList(value: unknown): ("log" | "port")[] {
if (!Array.isArray(value)) throw new Error("daemon.readyPending must be an array");
const result: ("log" | "port")[] = [];View on GitHub (pinned to 9690622007)
Solutions
- Use only the documented state strings: starting|running|ready|restarting|stopping|exited|failed
- Upgrade client and daemon together so both share the same state enum
- Normalize case before sending (states are lowercase)
- Map any new upstream state to an existing one until both sides are updated
Example fix
// before
snapshot = { id: "d1", state: "CRASHED", ... }
// after
snapshot = { id: "d1", state: "failed", ... } Defensive patterns
Strategy: validation
Validate before calling
const DAEMON_STATES = ["starting","running","ready","restarting","stopping","exited","failed"] as const;
type DaemonStateCheck = typeof DAEMON_STATES[number];
function isKnownState(v: unknown): v is DaemonStateCheck {
return typeof v === "string" && (DAEMON_STATES as readonly string[]).includes(v);
}
if (!isKnownState(snapshot.state)) throw new Error(`unknown state ${snapshot.state}`); Type guard
function isKnownState(v: unknown): v is "starting"|"running"|"ready"|"restarting"|"stopping"|"exited"|"failed" {
return typeof v === "string" && ["starting","running","ready","restarting","stopping","exited","failed"].includes(v);
} Try / catch
try {
const snap = parseDaemonSnapshot(raw);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Unknown daemon state")) {
// version mismatch: upgrade client/daemon or map the state to 'failed'
} else throw err;
} Prevention
- Keep the state enum in one shared module used by both client and daemon
- Always lowercase state strings before emitting them
- Add exhaustive switch handling with a default branch that logs unknown states
- Bump a protocol version whenever a new state is introduced
When it happens
Trigger: parseDaemonSnapshot or parseDaemonRpcResult receiving state values like "crashed", "STARTED" (case mismatch), "pending", or a localized/newer-format state string from a different daemon version.
Common situations: Client and daemon version skew introducing a new state; code that lowercases/uppercases states before sending; custom tooling writing its own state names into the snapshot.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- ${label} must be a string
- Unknown restart policy: ${policy}
- Unknown daemon signal: ${signal}
- daemon.readyPending must be an array
- Unknown readiness condition: ${String(item)}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/001870ff650149b3.
Report an issue: GitHub.