process.exit()

Terminates the Node.js process immediately with the given exit status code.

Signature
process.exit([code])
  • Added in Node.js v0.1.13

Parameters

ParameterTypeDescription
$codeoptionalinteger | string | null | undefined= 0The exit status to report to the shell. For string type, only integer strings (e.g. '1') are allowed. If omitted, Node.js uses 0 or the current value of process.exitCode if one has been set.

Return value

voidDoes not return. Control never comes back to the caller; the process terminates once every 'exit' listener has run.

Errors

Throws ERR_INVALID_ARG_TYPE if code is neither a number nor a string that represents an integer.

Changelog

  • PHP v20.0.0Only accepts a code of type number, or of type string if it represents an integer. Booleans, floats and non-numeric strings now throw instead of being coerced.

process.exit() is a hard stop. Node.js runs every 'exit' listener synchronously and then tears the process down, without waiting for timers, promises, open sockets, or in-flight writes to process.stdout.

When to reach for it

  • A signal handler that has already attempted a graceful shutdown and needs a hard deadline.
  • A CLI that must return a specific status from deep inside a callback where no error can usefully propagate.
  • A watchdog that must not leave a hung handle keeping the loop alive.

What trips people up

The classic bug is printing usage text and immediately calling exit(1). Writes to stdout are asynchronous when stdout is a pipe on POSIX, so the message can be truncated or lost entirely — the user sees a bare non-zero status and no explanation.

Almost every case people reach for exit() is better served by setting process.exitCode and simply letting the script end, or by throwing an uncaught error. A Node.js process exits on its own as soon as the event loop has nothing left to do.

Inside a Worker thread this stops that thread, not the whole process.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Force-terminate after a graceful shutdown timeout expires so a hung connection cannot block the deploy.
  • Return a non-zero status from a CLI validation failure so CI marks the step as failed.
  • Abort a migration script from inside a callback when the database schema is not what was expected.

Frequently asked questions

Why does console.log before process.exit not print?

Because writes to process.stdout are asynchronous when stdout is a pipe or socket on POSIX, and process.exit() terminates before the pending write is flushed. Set process.exitCode instead of calling exit(), or pass a callback to process.stdout.write() and exit from inside it.

What is the difference between process.exit() and process.exitCode?

process.exit() stops the process right now and abandons pending work, while process.exitCode only records the status to use whenever the process ends naturally. Prefer process.exitCode unless you specifically need to cut a hung process short.

How do I exit a Node.js script with an error code?

Assign process.exitCode = 1 and return from your function, letting the event loop drain. Use process.exit(1) only when you must terminate immediately, and be aware it can truncate output.

More examples

The truncation bug, and the fix

Run the first version with its output piped to another command (`node cli.js | cat`) and the usage line can vanish, because the pipe write had not been flushed when the process died. The second version produces the same exit status of 1 but the write always completes, because Node only exits after the loop is empty.

Example · javascript
// WRONG: the usage text may never reach the terminal.
function badUsage() {
  process.stdout.write('usage: report --since <date>\n');
  process.exit(1);
}

// RIGHT: set the status and let the process end naturally.
function goodUsage() {
  process.stdout.write('usage: report --since <date>\n');
  process.exitCode = 1;
  return; // no further work is scheduled, so Node exits on its own
}

if (!process.argv.includes('--since')) {
  goodUsage();
} else {
  console.log('running report');
}

Hard deadline on a graceful shutdown

This is the legitimate use of process.exit(): a last-resort deadline. On a clean shutdown the callback fires, the timer is cleared, and the process ends by itself with status 0. If a keep-alive connection refuses to close, the 10-second timer wins and exits with status 1 so the orchestrator knows the shutdown was not clean.

Example · javascript
const http = require('node:http');

const server = http.createServer((req, res) => res.end('ok'));
server.listen(3000);

function shutdown(signal) {
  console.log(`${signal} received, closing server`);

  const deadline = setTimeout(() => {
    console.error('connections did not drain in 10s, forcing exit');
    process.exit(1);
  }, 10_000);

  // Do not let the deadline itself hold the loop open.
  deadline.unref();

  server.close(() => {
    console.log('server closed cleanly');
    clearTimeout(deadline);
    // No exit() call: the loop is empty now, so Node exits with 0.
  });
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

Which code the shell actually sees

The argument passed to exit() overrides any previously assigned process.exitCode, and that final number is what the 'exit' listener receives. Calling exit() with no argument falls back to process.exitCode, which is why libraries can set the status without knowing how the program will eventually terminate.

Example · javascript
process.exitCode = 9;

process.on('exit', (code) => {
  console.log('listener sees:', code);
});

// An explicit argument always wins over process.exitCode.
process.exit(42);

// Output:
// listener sees: 42
//
// $ node codes.js; echo $?
// 42
//
// With `process.exit()` (no argument) instead, the shell would see 9.

Discussion

  • Be the first to comment on this lesson.