How does Express know a middleware is an error handler?
By its arity: an error-handling middleware takes four arguments (err, req, res, next). It must be registered after the routes, and errors reach it through next(err) or a thrown error in synchronous code.
Explain it simply
In a college office, most clerks handle normal forms. One special counter handles only complaints, and the sign on it says "complaints". In Express, the sign is the number of parameters. A function with four parameters is treated as the complaint counter, even if you never use next.
Example
const express = require("express");
const app = express();
app.get("/marks/:id", (req, res, next) => {
const id = Number(req.params.id);
if (Number.isNaN(id)) {
const err = new Error("id must be a number");
err.status = 400;
return next(err); // jump to the error handler
}
res.json({ id, marks: 82 });
});
// 404 for anything not matched above
app.use((req, res) => res.status(404).json({ error: "Not found" }));
// error handler: four parameters, registered last
app.use((err, req, res, next) => {
const status = err.status || 500;
res.status(status).json({
error: status === 500 ? "Something went wrong" : err.message,
});
});Rules:
- A thrown error in synchronous code reaches the handler automatically.
- Express 4 does not catch rejected promises in async handlers; call
next(err)or wrap them. Express 5 does forward them. - If headers were already sent, pass the error on with
next(err).
What to say in the interview
"Express treats a four-argument function as an error handler. I register it last, set the status from the error, hide internal messages for 500s, and pass errors with next(err), especially from async code in Express 4."
Common mistakes
- Registering the error handler before the routes.
- Removing the unused
nextargument and turning it into a normal middleware.