Custom Error Handling
Custom error handling allows you to intercept exceptions raised during request processing and return specific responses instead of the default error pages. This is managed through the errorhandler decorator or the register_error_handler method provided by the Scaffold class, which is inherited by both the Flask application and Blueprint objects.
Registering Error Handlers
To handle a specific HTTP status code or a custom Python exception, use the @app.errorhandler decorator.
from flask import Flask
from werkzeug.exceptions import HTTPException
app = Flask(__name__)
class DatabaseError(Exception):
"""Custom exception for database failures."""
pass
@app.errorhandler(404)
def page_not_found(error):
# error is an instance of NotFound
return "This page does not exist", 404
@app.errorhandler(DatabaseError)
def special_exception_handler(error):
# error is the instance of DatabaseError that was raised
return "Database connection failed", 500
Manual Registration
If you prefer not to use decorators, you can use the register_error_handler method directly:
def handle_forbidden(e):
return "Access Denied", 403
app.register_error_handler(403, handle_forbidden)
Handling Exception Subclasses
Error handlers are dispatched based on the exception's inheritance tree. If a handler is registered for a parent class, it will also catch any unregistered subclasses.
class ParentException(Exception):
pass
class ChildException(ParentException):
pass
@app.errorhandler(ParentException)
def handle_parent(e):
return "Caught by parent handler", 500
@app.route("/trigger")
def trigger():
# This will be caught by handle_parent
raise ChildException()
If both a parent and a child have registered handlers, the most specific handler (the child's) will be used.
Accessing the Original Exception in 500 Errors
When an unhandled exception occurs, Flask raises an InternalServerError (HTTP 500). If you register a handler for 500 or InternalServerError, you can access the original exception that triggered the failure via the original_exception attribute.
from werkzeug.exceptions import InternalServerError
@app.errorhandler(500)
def handle_500(e):
if e.original_exception is not None:
# Log or inspect the actual error (e.g., a KeyError or ValueError)
return f"Internal error caused by: {type(e.original_exception).__name__}", 500
return "Direct 500 error", 500
Blueprint-Specific Handlers
Blueprints can define their own error handlers. A handler registered on a blueprint will take precedence over an app-level handler if the error occurs within a route handled by that blueprint.
from flask import Blueprint
from werkzeug.exceptions import InternalServerError
bp = Blueprint("auth", __name__)
@bp.errorhandler(InternalServerError)
def handle_bp_500(e):
return "Auth Blueprint Error", 500
@app.errorhandler(InternalServerError)
def handle_app_500(e):
return "Global App Error", 500
In this scenario, an error in an auth route returns "Auth Blueprint Error", while errors elsewhere return "Global App Error".
Catch-all Exception Handlers
You can register very generic handlers for Exception or HTTPException to catch broad categories of errors.
from werkzeug.exceptions import HTTPException
@app.errorhandler(HTTPException)
def handle_all_http_errors(e):
"""Catch all Werkzeug HTTP exceptions (404, 401, 500, etc.)"""
return f"HTTP Error {e.code}", e.code
@app.errorhandler(Exception)
def handle_everything(e):
"""Catch-all for every unhandled Python exception."""
return "An unexpected error occurred", 500
Troubleshooting and Configuration
Registration Timing
All error handlers must be registered before the application starts handling requests. Attempting to register a handler after the first request has been dispatched will result in an error.
Exception Propagation
In development or testing modes, Flask's default behavior is often to propagate exceptions to the WSGI server so the debugger can catch them. This is controlled by the PROPAGATE_EXCEPTIONS configuration.
- If
app.config["PROPAGATE_EXCEPTIONS"]isTrue, your custom 500 handlers might be bypassed in favor of the interactive debugger. - If you want to test your error handlers specifically, ensure
PROPAGATE_EXCEPTIONSisFalse.
Trapping Exceptions
You can force Flask to re-raise exceptions instead of handling them using these flags:
TRAP_HTTP_EXCEPTIONS: IfTrue,HTTPExceptionsubclasses are re-raised.TRAP_BAD_REQUEST_ERRORS: IfTrue,BadRequestKeyError(common when accessing missing form data) is re-raised. This is enabled by default in debug mode.