Debugging & Error Handling
Flask provides a robust system for capturing exceptions, returning custom error responses, and providing detailed diagnostic information during development.
Registering Error Handlers
The primary way to handle errors is using the @app.errorhandler decorator. You can register handlers for specific HTTP status codes or for entire exception classes.
from flask import Flask, render_template
from werkzeug.exceptions import BadRequest
app = Flask(__name__)
# Handle 404 Not Found errors
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
# Handle specific exception classes
@app.errorhandler(BadRequest)
def handle_bad_request(e):
return "Custom bad request message", 400
# Handle custom application exceptions
class DatabaseError(Exception):
pass
@app.errorhandler(DatabaseError)
def special_exception_handler(e):
return 'Database connection failed', 500
Handling Unhandled Exceptions (500 Errors)
When an exception occurs that doesn't have a specific handler, Flask catches it and returns a 500 Internal Server Error. If you register a handler for 500 or InternalServerError, Flask will pass an instance of InternalServerError to your function.
The original exception that triggered the error is available via the original_exception attribute, as seen in tests/test_user_error_handler.py:
from werkzeug.exceptions import InternalServerError
@app.errorhandler(500)
def handle_500(e):
# e is always an InternalServerError here
if e.original_exception is not None:
# Access the actual error (e.g., a KeyError or DatabaseError)
return f"Error: {type(e.original_exception).__name__}", 500
return "Internal Server Error", 500
Blueprint-Specific Handlers
Error handlers registered on a Blueprint only apply to requests handled by that blueprint. To register a handler on a blueprint that affects the entire application, use @bp.app_errorhandler.
from flask import Blueprint
auth_bp = Blueprint("auth", __name__)
# Only handles 403s raised within the 'auth' blueprint
@auth_bp.errorhandler(403)
def handle_auth_forbidden(e):
return "Auth access denied", 403
# Handles 401s for the entire application
@auth_bp.app_errorhandler(401)
def handle_global_unauthorized(e):
return "Please log in", 401
Debugging Utilities
Flask includes several helpers in src/flask/debughelpers.py to improve the developer experience by providing more descriptive error messages.
Template Loading Diagnostics
If you are having trouble finding templates, enable EXPLAIN_TEMPLATE_LOADING. Flask will log every attempt it makes to locate a template, including which loaders were used and which blueprints were checked.
app.config['EXPLAIN_TEMPLATE_LOADING'] = True
Form Data and Redirects
In debug mode, Flask raises a FormDataRoutingRedirect if a routing redirect (like adding a trailing slash) would cause a browser to drop POST data. This helps prevent silent data loss during development.
File Upload Errors
If you try to access request.files['key'] but the form was not submitted with enctype="multipart/form-data", Flask raises a DebugFilesKeyError with a detailed explanation of how to fix the form.
Logging
Flask uses a standard Python logger named after the application's import_name. You can access it via app.logger.
The logger is configured in src/flask/logging.py to:
- Set the level to
DEBUGifapp.debugisTrue. - Log to
wsgi.errors(the web server's error log) orsys.stderr.
@app.route('/debug-log')
def debug_log():
app.logger.debug("This only shows if app.debug is True")
app.logger.error("This will show in the WSGI error stream")
return "Logged"
Configuration Reference
| Key | Default | Description |
|---|---|---|
PROPAGATE_EXCEPTIONS | None | If True, exceptions are re-raised instead of being handled by error handlers. Defaults to True in debug/testing. |
TRAP_HTTP_EXCEPTIONS | False | If True, HTTP exceptions are not handled and will propagate up the stack. |
TRAP_BAD_REQUEST_ERRORS | None | If True, KeyError exceptions from request data (like request.form) show the missing key in the response. |
EXPLAIN_TEMPLATE_LOADING | False | Logs detailed information about template lookup attempts. |
Troubleshooting
- Handlers not firing: Ensure the exception class you are handling is the one actually being raised. If you handle
Exception, it will catch everything, includingHTTPExceptionsubclasses. - Blueprint scope: Remember that
@bp.errorhandleris local to the blueprint. If a 404 occurs because no route matched at all, it won't be caught by a blueprint handler; it must be caught by an@app.errorhandler(404). - Redirects:
RequestRedirect(used for trailing slashes) is an internal routing exception and is not passed to error handlers. Flask handles these automatically to perform the redirect.