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

  1. Use only the documented state strings: starting|running|ready|restarting|stopping|exited|failed
  2. Upgrade client and daemon together so both share the same state enum
  3. Normalize case before sending (states are lowercase)
  4. 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

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


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