Set process.exitCode and let the script end on its own. Calling process.exit() stops the process immediately, before pending writes have flushed.
if (failures.length > 0) {
console.error(`${failures.length} checks failed`);
process.exitCode = 1;
}
When the event loop drains, Node exits with that code. Everything written to stdout and stderr gets there first.
Why exit() loses output
Writes to a pipe or a file are asynchronous. console.log hands the bytes to the stream and returns; the flush happens on a later tick. process.exit(1) doesn’t wait for that tick. Run the script in a terminal and it looks fine, because a TTY write is synchronous. Run it in CI, where stdout is a pipe, and the last lines vanish, which is usually the error message you needed.
When exit() is right
A hung process that you’ve already reported on, such as a server that failed to bind and now has open handles keeping the loop alive. Even then, write the message, then exit on the next tick:
console.error("Could not bind port 3030");
setImmediate(() => process.exit(1));
Gotchas
- An uncaught exception already sets the code to 1 and prints the stack. You don’t need to catch it just to set
exitCode. process.exitCodeset to a non-zero value is sticky. A laterprocess.exit()with no argument uses it.- Under
npm run, npm reports the script’s exit code as its own, so a1here fails the CI step as expected.