{"record":{"id":"001870ff650149b3","repo":"can1357/oh-my-pi","slug":"unknown-daemon-state-state","errorCode":null,"errorMessage":"Unknown daemon state: ${state}","messagePattern":"Unknown daemon state: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/protocol.ts","lineNumber":209,"sourceCode":"function 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}\n\nfunction restartPolicy(value: unknown): DaemonRestartPolicy {\n\tconst policy = stringValue(value, \"restart policy\");\n\tif (policy === \"no\" || policy === \"on-failure\" || policy === \"always\") return policy;\n\tthrow new Error(`Unknown restart policy: ${policy}`);\n}\n\nfunction daemonSignal(value: unknown): DaemonSignal {\n\tconst signal = stringValue(value, \"signal\");\n\tif (signal === \"SIGINT\" || signal === \"SIGTERM\" || signal === \"SIGHUP\") return signal;\n\tif (signal === \"SIGQUIT\" || signal === \"SIGKILL\") return signal;\n\tthrow new Error(`Unknown daemon signal: ${signal}`);\n}\n\nfunction readyPendingList(value: unknown): (\"log\" | \"port\")[] {\n\tif (!Array.isArray(value)) throw new Error(\"daemon.readyPending must be an array\");\n\tconst result: (\"log\" | \"port\")[] = [];","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/protocol.ts#L191-L227","documentation":"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.","triggerScenarios":"parseDaemonSnapshot or parseDaemonRpcResult receiving state values like \"crashed\", \"STARTED\" (case mismatch), \"pending\", or a localized/newer-format state string from a different daemon version.","commonSituations":"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.","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"],"exampleFix":"// before\nsnapshot = { id: \"d1\", state: \"CRASHED\", ... }\n// after\nsnapshot = { id: \"d1\", state: \"failed\", ... }","handlingStrategy":"validation","validationCode":"const DAEMON_STATES = [\"starting\",\"running\",\"ready\",\"restarting\",\"stopping\",\"exited\",\"failed\"] as const;\ntype DaemonStateCheck = typeof DAEMON_STATES[number];\nfunction isKnownState(v: unknown): v is DaemonStateCheck {\n  return typeof v === \"string\" && (DAEMON_STATES as readonly string[]).includes(v);\n}\nif (!isKnownState(snapshot.state)) throw new Error(`unknown state ${snapshot.state}`);","typeGuard":"function isKnownState(v: unknown): v is \"starting\"|\"running\"|\"ready\"|\"restarting\"|\"stopping\"|\"exited\"|\"failed\" {\n  return typeof v === \"string\" && [\"starting\",\"running\",\"ready\",\"restarting\",\"stopping\",\"exited\",\"failed\"].includes(v);\n}","tryCatchPattern":"try {\n  const snap = parseDaemonSnapshot(raw);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Unknown daemon state\")) {\n    // version mismatch: upgrade client/daemon or map the state to 'failed'\n  } else throw err;\n}","preventionTips":["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"],"tags":["validation","enum","ipc","protocol"],"backgroundTag":"invalid-enum-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}