Custom Error Handling and Exception Mapping
To register custom error handlers for specific HTTP status codes or Python exception classes in this project, use the errorhandler decorator provided by the Scaffold class (inherited by both Flask and Blueprint).
from flask import Flask, render_template
from werkzeug.exceptions import HTTPException
app = Flask(__name__)
# Registering by HTTP status code
@app.errorhandler(404)
def page_not_found(e):
# e is an instance of werkzeug.exceptions.NotFound
return render_template('404.html'), 404
# Registering by custom exception class
class ValidationException(Exception):
pass
@app.errorhandler(ValidationException)
def handle_validation_error(e):
# e is the instance of ValidationException raised
return {"error": str(e)}, 400
Error Registration Methods
The Scaffold class (found in src/flask/sansio/scaffold.py) provides two primary ways to register handlers:
errorhandler(code_or_exception): A decorator that takes either an integer HTTP status code or an exception class. It internally callsregister_error_handler.register_error_handler(code_or_exception, f): A method for non-decorator usage. This is useful when you want to attach a handler function defined elsewhere.
The project uses _get_exc_class_and_code to resolve the input into a tuple of (exception_class, status_code). If an integer is provided, it looks up the corresponding class in werkzeug.exceptions.default_exceptions.
Blueprint-Specific Handlers
You can register handlers that only trigger for requests handled by a specific Blueprint. This allows for modular error responses (e.g., JSON for an API blueprint and HTML for a frontend blueprint).
from flask import Blueprint
api_bp = Blueprint("api", __name__)
@api_bp.errorhandler(404)
def handle_api_404(e):
return {"error": "Resource not found"}, 404
If you need a blueprint to register a global handler that applies to the entire application, use the app_errorhandler method (defined in src/flask/sansio/blueprints.py):
@api_bp.app_errorhandler(500)
def handle_global_500(e):
return "A global server error occurred", 500
Handling Generic Exceptions
You can catch broad categories of errors by registering handlers for base classes like HTTPException or the root Exception class.
@app.errorhandler(HTTPException)
def handle_http_exception(e):
"""Return JSON instead of HTML for all HTTP errors."""
response = e.get_response()
response.data = f'{{"code": {e.code}, "name": "{e.name}"}}'
response.content_type = "application/json"
return response
@app.errorhandler(Exception)
def handle_unhandled_exception(e):
"""Catch-all for any unhandled Python exception."""
return "An unexpected error occurred", 500
Accessing the Original Exception
When handling a 500 error (Internal Server Error), the exception passed to the handler is often a werkzeug.exceptions.InternalServerError wrapper. You can access the original raised exception via the original_exception attribute.
@app.errorhandler(500)
def handle_500(e):
# e is InternalServerError
if e.original_exception is not None:
# Access the actual exception that caused the 500
error_type = type(e.original_exception).__name__
return f"Internal error caused by: {error_type}", 500
return "Direct 500 error", 500
Troubleshooting and Configuration
- Registration Timing: Error handlers must be registered before the application starts handling requests. The
Scaffoldmethods are decorated with@setupmethod, which ensures they are called during the configuration phase. - Resolution Order: Flask searches for handlers in the following order:
- Blueprint-specific handler for the status code.
- Application-wide handler for the status code.
- Blueprint-specific handler for the exception class.
- Application-wide handler for the exception class.
- Exception Trapping: If
app.config['TRAP_HTTP_EXCEPTIONS']is set toTrue, HTTP exceptions will propagate up instead of being caught by your handlers. - Propagation: If
app.config['PROPAGATE_EXCEPTIONS']isTrue(which is the default whenTESTINGisTrue), exceptions will bypass handlers and propagate to the WSGI server.