Error Middleware
title: "Error Middleware — Centralizing API Error Handling" description: "Error middleware centralizes API error handling by catching all exceptions and returning consistent error responses with appropriate status codes and trace information." date: 2026-06-28 lastmod: 2026-06-28 weight: 21 tags: [apis, error-handling] }
Error middleware is a centralized error handling layer that catches all exceptions from API handlers and returns consistent, structured error responses with proper status codes.
What You'll Learn
- Building error middleware in Express and Flask
- Mapping exceptions to status codes
- Error logging and monitoring
Why It Matters
Centralized error handling ensures every error returns a consistent format, reducing code duplication and preventing unhandled exceptions from leaking.
Code Examples
# Flask error middleware
class AppError(Exception):
status_code = 400
def __init__(self, message, code=None, status_code=None, payload=None):
super().__init__(message)
self.message = message
self.code = code or self.__class__.__name__
if status_code:
self.status_code = status_code
self.payload = payload or {}
def to_dict(self):
return {
"error": self.code,
"message": self.message,
**self.payload
}
@app.errorhandler(AppError)
def handle_app_error(error):
return jsonify(error.to_dict()), error.status_code
@app.errorhandler(404)
def handle_404(error):
return jsonify({
"error": "NOT_FOUND",
"message": "Endpoint not found"
}), 404
@app.errorhandler(500)
def handle_500(error):
trace_id = str(uuid.uuid4())
app.logger.error(f"Unhandled error {trace_id}: {error}")
return jsonify({
"error": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"trace_id": trace_id
}), 500
// Express error middleware
class AppError extends Error {
constructor(message, statusCode = 400, code = null) {
super(message);
this.statusCode = statusCode;
this.code = code || this.constructor.name;
}
}
function errorHandler(err, req, res, next) {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
error: err.code,
message: err.message
});
}
// Unhandled errors
const traceId = uuidv4();
console.error(`[${traceId}]`, err.stack);
res.status(500).json({
error: 'INTERNAL_ERROR',
message: 'An unexpected error occurred',
trace_id: traceId
});
}
app.use(errorHandler);
Common Mistakes
1. Not Catching Async Errors
Express doesn't catch async errors by default. Wrap async handlers.
2. Different Error Formats for Different Errors
Every error type should use the same base format.
3. Not Logging Errors with Context
Log the request method, URL, user ID, and error details.
4. Swallowing Errors Without Logging
Never catch errors without logging them first.
5. No Distinction Between Expected and Unexpected Errors
Expected errors (validation) vs unexpected (database down) need different handling.
Practice Questions
- Why use error middleware instead of try/catch in every handler?
- How do you handle async errors in Express?
- What information should be logged for errors?
- How do you map custom exceptions to HTTP status codes?
- Why should error types extend a base error class?
Answers:
- Centralized handling reduces duplication and ensures consistent responses.
- Wrap async route handlers with a catch function or use async middleware support.
- Trace ID, request path, method, user, error message, stack trace (internal).
- Each exception class defines its own status_code.
- So error middleware can catch one base type and handle all specific errors.
Challenge: Build a complete error handling middleware in your preferred framework. Support custom error classes, 404 catches, 500 fallbacks, and async error handling.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro