CodingNic

Error Handling

Passing Errors to Express with next()

Error Handling 10 min read

Passing Errors to Express with next()

Objectives

By the end of this lesson, you should be able to:

  • Call next(err) to report an error to Express
  • Explain why next(err) skips every regular middleware and route still ahead
  • Chain multiple error-handling middleware functions together

💡 Why this matters: Module 7 covered next() with no arguments, continuing to the next regular middleware. next(err), with an argument, means something completely different, it’s the one mechanism every error in this module ultimately flows through.

⚠️ A note on verification: every snippet and every response shown below was actually run and tested with real HTTP requests.

next(err) Skips Straight to Error Handling

javascript
app.get('/demo', (req, res, next) => {
  console.log('route handler ran');
  next(new Error('Something failed'));
});

// regular middleware, registered after
app.use((req, res, next) => {
  console.log('this regular middleware should be skipped');
  next();
});

// error-handling middleware: exactly 4 params
app.use((err, req, res, next) => {
  console.log('error handler ran:', err.message);
  res.status(500).json({ error: err.message });
});
bash
curl -w " [%{http_code}]" http://localhost:4409/demo

Server-side log:

text
route handler ran
error handler ran: Something failed

Response:

text
{"error":"Something failed"} [500]

Notice “this regular middleware should be skipped” never logs, calling next(err) (an argument, any truthy value, conventionally an Error object) tells Express this request has failed, and every remaining regular middleware and route is skipped entirely, Express jumps straight to the nearest error-handling middleware instead, the kind with exactly four parameters, (err, req, res, next). This four-parameter signature is how Express tells an error handler apart from a regular one, covered in full in Lesson 5.

Chaining Error Handlers

javascript
app.use((err, req, res, next) => {
  console.log('first error handler: logging only');
  next(err);
});

app.use((err, req, res, next) => {
  console.log('second error handler: sending response');
  res.status(500).json({ error: err.message });
});
text
first error handler: logging only
second error handler: sending response

An error-handling middleware can itself call next(err), passing the same error along to the next error-handling middleware in the chain, exactly like regular middleware passes control forward with next(). This enables splitting responsibilities, one error handler that only logs, followed by one that actually sends the response, useful in a larger app with dedicated logging infrastructure.

Where next(err) Typically Gets Called

Three places call next(err) in practice: inside a catch block after try/catch around synchronous or awaited code (Lessons 1 and 2), inside a regular callback when an operation fails (a database callback receiving an error as its first argument, for example), and, as of Express 5, automatically, whenever an async route handler’s returned promise rejects (Lesson 2), no manual call needed for that specific case, though it’s still the same underlying mechanism.

Try It

  1. Write a route that calls next(new Error('...')) directly, and confirm a regular app.use() middleware registered after it never runs.
  2. Add a second error-handling middleware after the first, have the first log the error and call next(err), and confirm the second one sends the actual response.
  3. Combine try/catch (Lesson 1) with next(err) in the catch block, and confirm the error still reaches your error-handling middleware correctly.
  4. Explain, in your own words, why Express distinguishes an error-handling middleware from a regular one by its parameter count rather than some other signal.

Recap

  • next(err) reports an error to Express, skipping every remaining regular middleware and route, jumping straight to error-handling middleware.
  • Error-handling middleware is identified by having exactly four parameters, (err, req, res, next).
  • Error handlers can chain, an error handler calling next(err) passes the error to the next error handler in line.

Next lesson: custom error classes, representing different kinds of failure with real, meaningful types.