can1357/oh-my-pi · error

Unknown daemon signal: ${signal}

Error message

Unknown daemon signal: ${signal}

What it means

Thrown by daemonSignal() when a daemon operation's signal field is a string but not one of the supported POSIX signals (SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGKILL). The daemon forwards only these validated signals to the child process.

Source

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

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;
}

function readySpec(value: unknown): DaemonReadySpec {
	const source = record(value, "ready");
	const log = optionalString(source.log, "ready.log");
	const port = optionalNumber(source.port, "ready.port");
	const host = optionalString(source.host, "ready.host");
	const timeoutMs = numberValue(source.timeoutMs, "ready.timeoutMs");

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the full uppercase names: SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGKILL
  2. Convert short/numeric names at the producer (e.g. map "9"→"SIGKILL", "TERM"→"SIGTERM")
  3. Drop unsupported signals; pick the closest supported one (e.g. SIGKILL instead of SIGSTOP)

Example fix

// before
operation = { type: "signal", id: "d1", signal: "TERM" }
// after
operation = { type: "signal", id: "d1", signal: "SIGTERM" }
Defensive patterns

Strategy: validation

Validate before calling

const SIGNALS = ["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGKILL"] as const;
type AllowedSignal = typeof SIGNALS[number];
function isAllowedSignal(v: unknown): v is AllowedSignal {
  return typeof v === "string" && (SIGNALS as readonly string[]).includes(v);
}
if (!isAllowedSignal(op.signal)) throw new Error(`unsupported signal: ${op.signal}`);

Type guard

function isAllowedSignal(v: unknown): v is "SIGINT"|"SIGTERM"|"SIGHUP"|"SIGQUIT"|"SIGKILL" { return typeof v === "string" && ["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGKILL"].includes(v); }

Try / catch

try {
  const op = parseDaemonOperation(raw);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Unknown daemon signal")) {
    // normalize short names (TERM→SIGTERM) or numeric ids (9→SIGKILL) and retry
  } else throw err;
}

Prevention

When it happens

Trigger: parseDaemonOperation receiving a signal operation with signal: "sigterm" (lowercase), "SIGUSR1" (unsupported), "TERM" (abbreviated), "9" (numeric), or a non-string value.

Common situations: Scripts using short signal names (TERM, KILL) or numeric IDs as accepted by kill(1); attempts to send signals the daemon intentionally does not support (SIGUSR1/2, SIGSTOP); lowercase signal names from cross-platform code.

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/3e9053c117927762. Report an issue: GitHub.