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
- Use the full uppercase names: SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGKILL
- Convert short/numeric names at the producer (e.g. map "9"→"SIGKILL", "TERM"→"SIGTERM")
- 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
- Use full uppercase POSIX names, not kill(1)-style abbreviations or numbers
- Whitelist signals in the UI/CLI so unsupported ones cannot be entered
- Reject or map signals like SIGUSR1/SIGSTOP before sending operations
- Share the signal union type between the daemon and all clients
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
- Unknown daemon state: ${state}
- Unknown restart policy: ${policy}
- Unknown readiness condition: ${String(item)}
- Unsupported language '{value}'. Supported: {}
- litterbox option ttl must be one of 1h, 12h, 24h, or 72h
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3e9053c117927762.
Report an issue: GitHub.