Asynchronous code and events
Handle asynchronous failures
Catch expected promise failures near the operation and let unknown fatal states stop the process cleanly.
8 minute lesson
Wrap awaited operations in a useful error boundary and add context before reporting the failure.
Handle expected failures near the operation that understands them. A missing optional config file might have a valid fallback. A rejected database query might become an HTTP 503. Preserve the original cause when adding context:
async function loadUser(id) {
try {
return await database.findUser(id)
} catch (error) {
throw new Error(`Cannot load user ${id}`, { cause: error })
}
}
At the command entry point, catch the final rejected promise and mark failure:
async function main() {
await runImport()
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
This boundary is for reporting a failed command, not inventing a result. It also prevents a “floating” promise whose rejection has no owner.
Current Node behavior treats an unhandled rejection as an uncaught exception by default. An uncaught exception means the application may have partially changed state or violated an invariant. Do not install an uncaughtException listener and continue serving requests as though nothing happened.
If you need last-resort observation without changing Node’s default crash behavior, uncaughtExceptionMonitor can synchronously record minimal diagnostic information. Avoid starting asynchronous recovery work that may never finish.
For a server, fatal handling and graceful operational shutdown are related but different. On a normal termination signal, drain work cleanly. After an unknown fatal error, perform only bounded, safe cleanup, stop accepting work, and let the process terminate. An external supervisor should restart it and apply backoff if failures repeat.
Do not call process.exit() immediately after console.error() unless abrupt termination is intentional; buffered output can be lost. Set process.exitCode or allow the uncaught error to terminate the process.
Your action is to create one expected rejected operation and one uncaught programmer error. Verify that the expected error gains context, both commands exit non-zero, and the programmer error does not leave a fake “healthy” server running.
Lesson completed