denoland/deno · error · TypeError
ERR_UNKNOWN_SIGNAL
ERR_UNKNOWN_SIGNAL
Error message
Unknown signal: ${signal} What it means
toDenoSignal() resolves a NUMERIC signal to a name by scanning os.signals (name → number). If no signal name maps to that number, ERR_UNKNOWN_SIGNAL is thrown with the number stringified. Only numbers present in the os.signals table (1–31-ish POSIX values like 9 for SIGKILL, 15 for SIGTERM) are accepted; 0 and out-of-range values like 99 are not.
Source
Thrown at ext/node/polyfills/internal/child_process.ts:1022
return "null";
case "inherit":
return "inherit";
case "ipc":
return "ipc_for_internal_use";
default:
throw new ERR_INVALID_ARG_VALUE("stdio", pipe);
}
}
function toDenoSignal(signal) {
const nodeSignals = os.signals;
if (typeof signal === "number") {
for (const name of new SafeArrayIterator(keys(nodeSignals))) {
if (nodeSignals[name] === signal) {
return name;
}
}
throw new ERR_UNKNOWN_SIGNAL(String(signal));
}
if (ReflectHas(nodeSignals, signal)) {
return signal;
}
// On Windows, os.signals only lists native signals. Accept any
// POSIX signal name so the caller can remap it to SIGTERM.
if (isWindows && StringPrototypeStartsWith(signal, "SIG")) {
return signal;
}
throw new ERR_UNKNOWN_SIGNAL(signal);
}
function keys(object) {
return ObjectKeys(object);
}
function copyProcessEnvToEnv(View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Use named signals: child.kill('SIGTERM'), child.kill('SIGKILL')
- Derive numbers only from os.signals / os.constants.signals so they always resolve (e.g. os.constants.signals.SIGTERM)
- For liveness checks, test child.exitCode !== null or wrap kill() in try/catch instead of using signal 0
Example fix
// before
child.kill(0); // probe for liveness
// after
const alive = child.exitCode === null && child.signalCode === null;
// and when terminating:
child.kill('SIGTERM'); Defensive patterns
Strategy: validation
Validate before calling
import os from 'node:os';
function toSignalName(sig) {
if (typeof sig === 'number') {
const entry = Object.entries(os.signals).find(([, n]) => n === sig);
if (!entry) throw new Error(`unsupported signal number ${sig}`);
return entry[0];
}
return sig;
} Type guard
import os from 'node:os'; const isValidSignalNumber = (n) => typeof n === 'number' && Object.values(os.signals).includes(n);
Try / catch
try { child.kill(sig); } catch (err) {
if (err?.code === 'ERR_UNKNOWN_SIGNAL') {
child.kill('SIGTERM'); // safe portable default
}
} Prevention
- Prefer named signals ('SIGTERM', 'SIGKILL') over numbers
- Use signal 0 only via try/catch liveness probes, never as a real kill target here
- Validate numbers against os.signals before forwarding them from other systems
When it happens
Trigger: child.kill(0) used as a liveness probe (signal 0 has no named entry); child.kill(99); signal values computed from arithmetic or read from config/protocol fields without validation.
Common situations: Porting Unix liveness checks that rely on kill(pid, 0) semantics; signal numbers forwarded from another service or IPC message; assuming any 1–64 number is valid because Linux accepts it at the syscall level.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/55da507ec6cf4d3a.
Report an issue: GitHub.