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)

ClassCodeName
BadRequest400Bad Request
Unauthorized401Unauthorized
Forbidden403Forbidden
NotFound404Not Found
MethodNotAllowed405Method Not Allowed
NotAcceptable406Not Acceptable
Conflict409Conflict
Gone410Gone
LengthRequired411Length Required
RequestEntityTooLarge413Request Entity Too Large
UnsupportedMediaType415Unsupported Media Type
UnprocessableEntity422Unprocessable Entity
TooManyRequests429Too Many Requests
InternalServerError500Internal Server Error
NotImplemented501Not Implemented
BadGateway502Bad Gateway
ServiceUnavailable503Service 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