Flask Error Handling
Flask provides a flexible error handling mechanism that can catch and handle various errors in the application.
Good error handling is not just about "no bugs" — it is about giving users a friendly experience when bugs do occur.
This chapter introduces how to customize error pages and manage logs in Flask.
Custom error page
By default, Flask displays a simple black-background, white-text page for errors such as 404.
Usage@app.errorhandlerDecorators can be used to customize error pages for specific HTTP status codes:
Example
app = Flask(__name__)
# Handle 404 error - page not found
@app.errorhandler(404)
def page_not_found(error):
# Note: must explicitly return status code 404
return render_template("404.html"), 404
# Handle 500 error - internal server error
@app.errorhandler(500)
def internal_error(error):
return render_template("500.html"), 500
# Handle 403 error - access forbidden
@app.errorhandler(403)
def forbidden(error):
return render_template("403.html"), 403
The corresponding 404 template:
Example
<!DOCTYPE html>
<html>
<head>
<title>404 - Page Not Found</title>
<style>
body { text-align: center; padding-top: 60px; font-family: Arial; }
h1 { font-size: 72px; color: #e74c3c; margin: 0; }
p { color: #666; font-size: 18px; }
a { color: #3498db; }
</style>
</head>
<body>
<h1>404</h1>
<p>Sorry, the page you visited does not exist.</p>
<p><a href="/">Back to Home</a></p>
</body>
</html>
InerrorhandlerIn the decorated function,returnYou must explicitly specify the status code. If you don't write it, 404, Flask will return a 200 status code by default, and browsers and search engines won't treat it as an error page.
Exception class registration
Besides the status code,@app.errorhandlerYou can also directly register exception classes:
Example
@app.errorhandler(HTTPException)
def handle_http_exception(error):
"""Handle all HTTP exceptions uniformly (e.g., 400, 401, 403, 404, etc.)"""
return f"""
<h1>HTTP Error {error.code}</h1>
<p>{error.description}</p>
<p><a href="/">Back to EXAMPLE homepage</a></p>
""", error.code
Using abort to trigger errors
In your view function, useabort()to actively trigger an HTTP error:
Example
# Simulate an article database
articles = {
1: {"title": Flask Tutorial},
2: {"title": "Python Basics"},
}
@app.get("/article/<int:article_id>")
def view_article(article_id):
article = articles.get(article_id)
if article is None:
# Article does not exist, return 404
# The description parameter will be passed to errorhandler
abort(404, description=f"Article ID {article_id} does not exist")
return f"<h1>{article['title']}</h1>"
@app.get("/admin")
def admin_panel():
# User without admin permissions accesses the admin backend
abort(403, description="You do not have admin permissions")
Logging
Flask uses the Python standard library'sloggingModule, throughapp.loggerThen you can log the record.
Logging is crucial for troubleshooting — don't just useprint(), as logs provide richer context and better control.
Example
app = Flask(__name__)
@app.route("/")
def index():
# Different log levels
app.logger.debug("Access homepage") # Debug information, usually not output in production
app.logger.info("User from IP: %s", request.remote_addr) # General information
app.logger.warning("Abnormal access pattern detected") # Warning: needs attention but does not affect operation
app.logger.error("Database connection failed") # Error: functionality is affected
app.logger.critical("Insufficient disk space, service is about to stop") # Critical error: service may stop
return "OK"
Log levels (from low to high):
| Level | Use Case |
|---|---|
| DEBUG | Detailed information during development debugging is not output in the production environment by default. |
| INFO | General runtime information, such as request logs and service startup |
| WARNING | Warning messages, potential issues that don't affect current operation |
| ERROR | Error messages, a feature failed but the service is still running |
| CRITICAL | Critical errors, may cause the service to stop |
In Debug mode, Flask's log level is automatically set toDEBUG. In production environments, set it as needed toINFOorWARNING, to avoid outputting too much useless information.
Configure log output
Write logs to a file for easier post-hoc analysis and troubleshooting:
Example
from logging.handlers import RotatingFileHandler
# Configure file log handler: rotate after the log file reaches 10MB
handler = RotatingFileHandler(
"example_app.log", # Log file path
maxBytes=10 * 1024 * 1024, # Maximum single file size 10MB
backupCount=5 # Keep the last 5 backups
)
# Set log format
handler.setFormatter(logging.Formatter(
"[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
))
# Add handler to Flask's logger
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)
Catching unhandled exceptions
Usageapp.errorhandler(500)You can catch unhandled exceptions and record them in logs:
Example
@app.errorhandler(500)
def internal_error(error):
# Log the full exception stack trace
app.logger.error("Internal server error:\n%s", traceback.format_exc())
# Show a friendly error page to the user
return """
<h1>500 - Internal Server Error</h1>
<p>Sorry, the server encountered an unexpected error. We have recorded the problem. Please try again later.</p>
<p>If the problem persists, please contact EXAMPLE technical support.</p>
""", 500
Quick reference for common HTTP error codes
| Status Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Malformed request parameters, missing required fields |
| 401 | Unauthorized | User not logged in or authentication failed |
| 403 | Forbidden | User is logged in but has no access permission |
| 404 | Not Found | Resource (article, user, etc.) does not exist |
| 405 | Method Not Allowed | Using the wrong HTTP method (e.g., using GET to access a POST endpoint) |
| 500 | Internal Server Error | Unhandled errors such as code exceptions and database connection failures |