{"record":{"id":"566d94ba2d46cc07","repo":"can1357/oh-my-pi","slug":"unknown-restart-policy-policy","errorCode":null,"errorMessage":"Unknown restart policy: ${policy}","messagePattern":"Unknown restart policy: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/protocol.ts","lineNumber":215,"sourceCode":"\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\")[] = [];\n\tfor (const item of value) {\n\t\tif (item !== \"log\" && item !== \"port\") throw new Error(`Unknown readiness condition: ${String(item)}`);\n\t\tresult.push(item);\n\t}\n\treturn result;\n}","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/protocol.ts#L197-L233","documentation":"Thrown by restartPolicy() when a daemon spec's restart policy string is not one of 'no', 'on-failure', or 'always'. The policy drives the daemon supervisor, so an unrecognized value is rejected at parse time rather than silently defaulting.","triggerScenarios":"parseDaemonSpec receiving restartPolicy values like \"unless-stopped\" (Docker-style), \"never\", \"ALWAYS\" (case mismatch), \"on_failure\" (underscore), or null/undefined in the spec.","commonSituations":"Copying restart-policy vocabulary from Docker/systemd into daemon config; YAML unquoted 'no' being parsed as boolean false in some formats then stringified differently; typos like 'onfailure'.","solutions":["Use exactly one of: \"no\", \"on-failure\", \"always\"","Quote the value in YAML configs so \"no\" is not coerced to boolean false","Normalize case/underscores to the kebab-case enum at the producer"],"exampleFix":"// before\nparseDaemonSpec({ id: \"d1\", command: [\"srv\"], restartPolicy: \"unless-stopped\" })\n// after\nparseDaemonSpec({ id: \"d1\", command: [\"srv\"], restartPolicy: \"always\" })","handlingStrategy":"validation","validationCode":"const POLICIES = [\"no\", \"on-failure\", \"always\"] as const;\ntype RestartPolicyCheck = typeof POLICIES[number];\nfunction isValidPolicy(v: unknown): v is RestartPolicyCheck {\n  return typeof v === \"string\" && (POLICIES as readonly string[]).includes(v);\n}\nif (!isValidPolicy(spec.restartPolicy)) throw new Error(`bad restartPolicy: ${spec.restartPolicy}`);","typeGuard":"function isValidPolicy(v: unknown): v is \"no\"|\"on-failure\"|\"always\" { return typeof v === \"string\" && [\"no\",\"on-failure\",\"always\"].includes(v); }","tryCatchPattern":"try {\n  const spec = parseDaemonSpec(raw);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Unknown restart policy\")) {\n    // map Docker-style policies (unless-stopped → always) and retry\n  } else throw err;\n}","preventionTips":["Quote 'no' in YAML configs so it is not parsed as boolean false","Only use the three documented values; translate external policy vocab at the edge","Centralize the policy union type and import it in both config loading and sending code","Validate configs at load time with the union type rather than at daemon parse time"],"tags":["validation","enum","configuration","ipc"],"backgroundTag":"invalid-enum-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}