Skip to content

Error Handling Express

DodaTech 2 min read

title: "Error Handling in Express — Building Robust Middleware Stacks" description: "Error handling in Express.js uses centralized error middleware to catch exceptions, return consistent responses, and log errors with trace IDs." date: 2026-06-28 lastmod: 2026-06-28 weight: 29 tags: [apis, error-handling] }

Express error handling uses specialized four-argument middleware functions to catch errors from all routes and return consistent, structured JSON error responses.

What You'll Learn

  • Express error middleware patterns
  • Handling sync and async errors
  • Custom error classes

Why It Matters

Express doesn't catch async errors by default. Without proper setup, unhandled promise rejections crash the server.

Code Examples

// Express error handling setup
const express = require('express');
const app = express();

// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Custom error class
class AppError extends Error {
  constructor(message, statusCode = 400, code = null) {
    super(message);
    this.statusCode = statusCode;
    this.code = code || 'APP_ERROR';
    this.isOperational = true;
  }
}

// Async route with error handling
app.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await db.findUser(req.params.id);
  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }
  res.json(user);
}));

// 404 catch-all
app.use((req, res) => {
  res.status(404).json({
    error: 'NOT_FOUND',
    message: `Route ${req.method} ${req.path} not found`
  });
});

// Central error handler
app.use((err, req, res, next) => {
  const traceId = uuidv4();
  const statusCode = err.statusCode || 500;
  const code = err.code || 'INTERNAL_ERROR';

  console.error(`[${traceId}]`, err.stack);

  res.status(statusCode).json({
    error: code,
    message: err.isOperational ? err.message : 'An unexpected error occurred',
    trace_id: traceId
  });
});

Common Mistakes

1. Not Wrapping Async Routes

Express 4 doesn't catch async errors. Always wrap async handlers.

2. Error Middleware Not Last

Error middleware must be the last middleware registered.

3. Not Differentiating Operational vs Programming Errors

Operational errors (validation) are safe to report; programming errors should be generic.

4. Missing 404 Catch-All

Without a 404 handler, unmatched routes return HTML or nothing.

5. Not Exiting on Uncaught Errors

Use process.on('uncaughtException') and process.on('unhandledRejection') for safety.

Practice Questions

  1. How many arguments does Express error middleware take?
  2. Why must async routes be wrapped?
  3. What is the difference between operational and programming errors?
  4. Where should error middleware be placed in the middleware stack?
  5. How do you handle uncaught exceptions in Node.js?

Answers:

  1. Four: (err, req, res, next).
  2. Express doesn't catch promise rejections from async functions.
  3. Operational errors are expected (validation); programming errors are bugs (undefined).
  4. Last, after all routes and other middleware.
  5. With process.on('uncaughtException') and process.on('unhandledRejection') handlers.

Challenge: Build a complete Express error handling system with async wrappers, custom error classes, 404 handler, centralized error middleware, and process-level exception handlers.

FAQ

Does Express 5 handle async errors automatically?

: Yes. Express 5 catches promise rejections from async route handlers.

Should I use try-catch in every route handler?

: No. Use the asyncHandler wrapper pattern for cleaner code.

How do I test error middleware?

: Use integration tests that trigger errors and check the response format.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro