can1357/oh-my-pi · error

Unknown restart policy: ${policy}

Error message

Unknown restart policy: ${policy}

What it means

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.

Source

Thrown at packages/coding-agent/src/launch/protocol.ts:215

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")[] = [];
	for (const item of value) {
		if (item !== "log" && item !== "port") throw new Error(`Unknown readiness condition: ${String(item)}`);
		result.push(item);
	}
	return result;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Use exactly one of: "no", "on-failure", "always"
  2. Quote the value in YAML configs so "no" is not coerced to boolean false
  3. Normalize case/underscores to the kebab-case enum at the producer

Example fix

// before
parseDaemonSpec({ id: "d1", command: ["srv"], restartPolicy: "unless-stopped" })
// after
parseDaemonSpec({ id: "d1", command: ["srv"], restartPolicy: "always" })
Defensive patterns

Strategy: validation

Validate before calling

const POLICIES = ["no", "on-failure", "always"] as const;
type RestartPolicyCheck = typeof POLICIES[number];
function isValidPolicy(v: unknown): v is RestartPolicyCheck {
  return typeof v === "string" && (POLICIES as readonly string[]).includes(v);
}
if (!isValidPolicy(spec.restartPolicy)) throw new Error(`bad restartPolicy: ${spec.restartPolicy}`);

Type guard

function isValidPolicy(v: unknown): v is "no"|"on-failure"|"always" { return typeof v === "string" && ["no","on-failure","always"].includes(v); }

Try / catch

try {
  const spec = parseDaemonSpec(raw);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Unknown restart policy")) {
    // map Docker-style policies (unless-stopped → always) and retry
  } else throw err;
}

Prevention

When it happens

Trigger: parseDaemonSpec receiving restartPolicy values like "unless-stopped" (Docker-style), "never", "ALWAYS" (case mismatch), "on_failure" (underscore), or null/undefined in the spec.

Common situations: 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'.

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/566d94ba2d46cc07. Report an issue: GitHub.