Flask Cheatsheet
Error Handling
Use this Flask reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
HTTP Error Handlers
from flask import render_template, jsonify @app.errorhandler(404) def not_found(e): return render_template("errors/404.html"), 404 @app.errorhandler(403) def forbidden(e): return render_template("errors/403.html"), 403 @app.errorhandler(500) def internal_error(e): return render_template("errors/500.html"), 500
The error object e is a werkzeug.exceptions.HTTPException with:
e.code # 404 e.name # "Not Found" e.description # default description string
abort() — Raise HTTP Errors
from flask import abort abort(400) # Bad Request abort(401) # Unauthorized abort(403) # Forbidden abort(404) # Not Found abort(405) # Method Not Allowed abort(409) # Conflict abort(410) # Gone abort(422) # Unprocessable Entity abort(429) # Too Many Requests abort(500) # Internal Server Error abort(404, "Post not found") # custom description abort(Response("custom body", 418)) # abort with a full Response object
abort() raises an exception immediately — code after it does not run.
Catching All HTTP Exceptions
from werkzeug.exceptions import HTTPException @app.errorhandler(HTTPException) def http_exception(e): return jsonify({ "error": e.name, "description": e.description, "status": e.code, }), e.code
This handler runs for all HTTP errors not caught by a more specific handler.
Catching Any Exception
@app.errorhandler(Exception) def unhandled_exception(e): app.logger.exception("Unhandled exception") return render_template("errors/500.html"), 500
In debug mode, Flask shows the interactive debugger instead of calling exception handlers. Handlers only run in production (
DEBUG=False).
Blueprint-Level Error Handlers
Blueprint handlers only fire for errors raised within that blueprint (Flask 2.3+):
@api_bp.errorhandler(404) def api_not_found(e): return jsonify({"error": "Not Found"}), 404 @api_bp.errorhandler(Exception) def api_error(e): return jsonify({"error": str(e)}), 500
Use app_errorhandler on a blueprint to register an app-wide handler:
@api_bp.app_errorhandler(404) def global_not_found(e): return jsonify({"error": "Not Found"}), 404
Custom Exception Classes
class AppError(Exception): status_code = 400 def __init__(self, message, status_code=None, payload=None): super().__init__(message) self.message = message if status_code is not None: self.status_code = status_code self.payload = payload def to_dict(self): rv = dict(self.payload or {}) rv["error"] = self.message return rv class NotFoundError(AppError): status_code = 404 class ValidationError(AppError): status_code = 422 class AuthError(AppError): status_code = 401 @app.errorhandler(AppError) def handle_app_error(e): return jsonify(e.to_dict()), e.status_code # Usage in views: @app.get("/items/<int:id>") def get_item(id): item = db.session.get(Item, id) if not item: raise NotFoundError(f"Item {id} not found") if not current_user.can_view(item): raise AuthError("Access denied") return jsonify(item.to_dict())
Logging
Flask uses standard Python logging. The app logger is app.logger (name = app name).
app.logger.debug("Debug info: %s", value) app.logger.info("User %s logged in", user.id) app.logger.warning("Rate limit approaching for %s", ip) app.logger.error("Failed to send email: %s", exc) app.logger.exception("Unhandled error") # logs traceback at ERROR level
Configuring Logging
import logging from logging.handlers import RotatingFileHandler if not app.debug: handler = RotatingFileHandler( "logs/app.log", maxBytes=10_000_000, backupCount=5 ) handler.setLevel(logging.WARNING) handler.setFormatter(logging.Formatter( "%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]" )) app.logger.addHandler(handler) app.logger.setLevel(logging.WARNING)
Logging to Syslog / External Services
from logging.handlers import SysLogHandler syslog = SysLogHandler(address=("logs.example.com", 514)) syslog.setLevel(logging.ERROR) app.logger.addHandler(syslog)
Request Logging
import time @app.before_request def start_timer(): g.start = time.perf_counter() @app.after_request def log_request(response): duration = time.perf_counter() - g.start app.logger.info( "%s %s %s %.2fms", request.method, request.path, response.status_code, duration * 1000, ) return response
Common HTTP Exception Classes (Werkzeug)
| Class | Code | Name |
|---|---|---|
BadRequest | 400 | Bad Request |
Unauthorized | 401 | Unauthorized |
Forbidden | 403 | Forbidden |
NotFound | 404 | Not Found |
MethodNotAllowed | 405 | Method Not Allowed |
NotAcceptable | 406 | Not Acceptable |
Conflict | 409 | Conflict |
Gone | 410 | Gone |
LengthRequired | 411 | Length Required |
RequestEntityTooLarge | 413 | Request Entity Too Large |
UnsupportedMediaType | 415 | Unsupported Media Type |
UnprocessableEntity | 422 | Unprocessable Entity |
TooManyRequests | 429 | Too Many Requests |
InternalServerError | 500 | Internal Server Error |
NotImplemented | 501 | Not Implemented |
BadGateway | 502 | Bad Gateway |
ServiceUnavailable | 503 | Service Unavailable |
from werkzeug.exceptions import NotFound, Forbidden, UnprocessableEntity raise NotFound("That page does not exist") raise Forbidden() raise UnprocessableEntity("Validation failed")
Error Pages in Templates
templates/errors/ 404.html 403.html 500.html
{# templates/errors/404.html #}
{% extends "base.html" %}
{% block content %}
<h1>404 — Page Not Found</h1>
<p>{{ e.description }}</p>
<a href="{{ url_for('index') }}">Go home</a>
{% endblock %}@app.errorhandler(404) def not_found(e): return render_template("errors/404.html", e=e), 404
Propagating Exceptions in Testing
app.config["TESTING"] = True app.config["PROPAGATE_EXCEPTIONS"] = True # exceptions reach the test, not the handler
Sentry Integration
pip install sentry-sdk[flask]
import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration sentry_sdk.init( dsn="https://your-dsn@sentry.io/project", integrations=[FlaskIntegration()], traces_sample_rate=0.2, send_default_pii=False, )
Sentry captures unhandled exceptions automatically. You can also capture manually:
from sentry_sdk import capture_exception, capture_message try: risky_operation() except Exception as e: capture_exception(e) raise