Avoid process.exit
ID |
javascript.avoid_process_exit |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
best-practice, reliability |
Description
Reports calls to process.exit(…). Node.js’s process.exit terminates the process synchronously — pending I/O (logs, file writes, network flushes), finally cleanup, and graceful-shutdown handlers all stop running. Library code in particular must never call process.exit: it removes the host application’s chance to handle the error gracefully.
Rationale
-
Open file descriptors, in-flight HTTP requests, and pino/winston buffered logs are silently dropped.
-
Any registered
process.on("exit", …)handler still runs but with no time for asynchronous work. -
Tests that import the module crash unless they monkey-patch
process.exitor wrap with subprocesses — a major test-suite headache. -
The exit code becomes implicit, scattered across the codebase, and hard to audit.
// Bad - drops pending log writes, breaks graceful shutdown
if (configBroken) {
console.error("config invalid");
process.exit(1);
}
Remediation
Throw an error and let the top-level handler decide the exit code, or set a non-zero process.exitCode and return from main so the runtime exits cleanly after pending I/O drains.
// Good - clean shutdown
if (configBroken) {
throw new ConfigError("config invalid");
}
// Or, in the entry-point only:
async function main() {
try {
await run();
} catch (err) {
log.error(err);
process.exitCode = 1;
}
}