Skip to the solution
MediumBackend7 min read

What is the difference between operational and programmer errors in Node.js?

206 views

The problem

Question presented to candidate: "Your Node service catches every error at the top level and returns a 500 without crashing, no matter what went wrong. Is that a good practice?"

What a strong answer should cover:

  • πŸ“Œ Operational errors are expected, anticipated failures in a correctly-written program: a database connection timing out, a file not existing, a client sending invalid input, a downstream API returning a 503. The program's logic is fine; the world outside it did something the code was written to expect.
  • πŸ“Œ Programmer errors are bugs: calling a method on undefined, a typo in a variable name, an incorrect assumption about a function's return shape. The program's logic itself is wrong.
  • The reason this distinction matters practically: an operational error should be caught and handled β€” return a 4xx/5xx response, retry, log it, and keep the process running. A programmer error means the process is in an unknown state β€” continuing to run risks corrupting data or serving wrong results silently, and the safer response is to let the process crash (and let a supervisor like a container orchestrator or process manager restart it fresh).
  • This maps directly onto process.on("uncaughtException", ...) and process.on("unhandledRejection", ...): the Node docs' own guidance is to use these as a last-resort log-and-exit point for bugs that were never caught, not as a general-purpose safety net to keep the process alive no matter what.
  • A useful implementation pattern: a custom OperationalError class (or an isOperational flag on thrown errors) lets a top-level handler distinguish "this is an anticipated failure, respond and move on" from "this is a bug, this process should not keep serving traffic."
  • The common anti-pattern this distinction is meant to prevent: wrapping literally everything in try/catch and always returning a generic 500, which masks real bugs as if they were routine failures and can leave the process silently running in a corrupted state indefinitely.

Clarifying questions expected:

  • "Does the process have a supervisor (Kubernetes, PM2, systemd) that will restart it if it exits?" β€” crashing on a programmer error is only a safe strategy if something restarts the process.
  • "Is this error path something a client can trigger repeatedly with normal usage, or a one-off bug?" β€” decides whether it is truly operational or actually a programmer error masquerading as one.

Code / implementation expected: Yes β€” a custom error class plus the branching logic in a handler is the clearest way to show the distinction in code, not just describe it.

  • error-handling
  • custom-errors
  • 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

7 min read Β· 8 sections

Read the code

JavaScriptOperationalError class, the branching handler, and a real crash-on-bug exit code
class OperationalError extends Error {
  constructor(message, { statusCode = 500 } = {}) {
    super(message);
    this.name = "OperationalError";
    this.statusCode = statusCode;
    this.isOperational = true;
  }
}

function readConfig(id) {
  if (id === "missing") throw new OperationalError(`Config ${id} not found`, { statusCode: 404 });
  if (id === "bug") return undefined.someMethod(); // a real bug
  return { id, value: 42 };
}

function handle(id) {
  try {
    console.log("handled ok:", readConfig(id));
  } catch (err) {
    if (err instanceof OperationalError) {
      console.log(`[operational] ${err.statusCode} ${err.message}`); // caught, respond, keep running
    } else {
      console.log(`[programmer error] ${err.constructor.name}: ${err.message}`);
      throw err; // rethrow β€” let this crash the process
    }
  }
}

handle("missing"); // [operational] 404 Config missing not found
// handle("bug") rethrows a TypeError instead of being swallowed

// The last-resort boundary for anything that still escapes:
process.on("uncaughtException", (err) => {
  console.log("uncaughtException handler ran:", err.message);
  process.exit(1); // log and exit β€” a supervisor restarts the process
});
// Verified: exit code was 1, not 0, when this path actually ran.

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 106 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

How do you handle streaming multipart file uploads (busboy/multer)?

Continue