Skip to the solution
MediumLow-Level Design6 min read

How do you handle uncaught exceptions and unhandled promise rejections in Node.js?

423 views

The problem

Question presented to candidate: "A Promise rejects and nothing ever calls .catch() on it. Does that crash your Node process today, and has that behavior always been true?"

What a strong answer should cover:

  • process.on("uncaughtException", ...) and process.on("unhandledRejection", ...) are two distinct events, for two distinct failure shapes: a genuine synchronous throw that propagates all the way up uncaught, versus a rejected Promise that no code ever attached a .catch()/await-try/catch to.
  • πŸ“Œ Verified, version-relevant behavior: with no handler registered at all, an unhandled Promise rejection crashes the process immediately, with a nonzero exit code β€” this is Node's modern default (since Node 15), not merely a warning as in some older versions; a precise answer does not assume the older, warn-only behavior without checking the actual Node version in use.
  • Both events are correctly understood as a last-resort safety net, not a general-purpose error-handling mechanism β€” the right place to handle an error is as close as possible to where it actually occurs (a try/catch, a .catch(), a centralized Express error middleware), not by relying on a process-wide handler to catch everything after the fact.
  • The standard, correct pattern for these handlers: log the error with full context, then deliberately exit the process (process.exit(1), or let the crash proceed) so a supervisor (a container orchestrator, pm2, systemd) restarts it fresh β€” connecting directly to the operational-vs-programmer-errors question, since reaching this handler at all generally signals an unknown, untrusted program state.
  • A precise answer names the real, common cause of unhandledRejection specifically: an async function called without awaiting it and with no .catch() attached to the resulting Promise β€” the call still runs, but nothing observes a later rejection.
  • process.on("uncaughtExceptionMonitor", ...) is a related, less commonly known event β€” it fires alongside uncaughtException without suppressing Node's own default handling, useful specifically for observability/logging without altering the crash behavior itself.

Clarifying questions expected:

  • "Which Node version β€” does the codebase rely on older warn-only unhandledRejection behavior, or the current terminate-by-default behavior?" β€” genuinely different, and worth confirming rather than assuming.
  • "Is the goal catching these as a last resort, or actually preventing them by fixing the missing .catch()/try/catch at the source?" β€” the latter is almost always the better fix.

Code / implementation expected: Yes β€” demonstrating both events firing distinctly, and the real crash-with-no-handler default behavior, is the concrete, convincing part of the answer.

  • errors
  • process
  • best-practices

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 9 sections

Read the code

JavaScriptuncaughtException and unhandledRejection firing distinctly, and the real crash-with-no-handler default behavior
// With handlers attached β€” both fire for their own distinct failure shape:
process.on("uncaughtException", (err, origin) => {
  console.log("uncaughtException:", err.message, "| origin:", origin);
});
process.on("unhandledRejection", (reason) => {
  console.log("unhandledRejection:", reason.message);
});

Promise.reject(new Error("a rejected promise nobody caught"));
setTimeout(() => { throw new Error("a synchronous throw nobody caught"); }, 50);
// unhandledRejection: a rejected promise nobody caught
// uncaughtException: a synchronous throw nobody caught | origin: uncaughtException

// --- Separately, with NO handler at all: ---
// Promise.reject(new Error("nobody catches this"));
// $ node script.js; echo "exit code: $?"
// Error: nobody catches this
//     at ...
// exit code: 1   <- crashed immediately, the modern default

// The most common real cause:
async function doWork() { throw new Error("boom"); }
doWork(); // called without await, no .catch() β€” genuinely unhandled

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 92 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

What are some common security best practices in Node.js applications?

Continue