AtomLearnAtomLearn
DashboardGoalsPathAchievementsReviewSign In
DashboardFlaskError Handlers & Middleware
FlaskNot Started

Error Handlers & Middleware

Centralized error handling, custom exceptions, and before/after request hooks

0%
Knowledge0%
Learn & Drill
Fluency0%
Drill & Speed
Retention0%
Mastery & Review
Confidence0%
All modes
Practice

Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.

Upgrade
Knowledge
Fluency
Retention

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

Production Flask apps need consistent responses and cross-cutting behavior via .

Registering error handlers:

python from flask import Flask, jsonify app = Flask(__name__) @app.errorhandler(404) def not_found(error): return jsonify({"error": "Resource not found"}), 404 @app.errorhandler(405) def method_not_allowed(error): return jsonify({"error": "Method not allowed"}), 405 @app.errorhandler(500) def server_error(error): return jsonify({"error": "Internal server error"}), 500

Custom exception classes:

python class APIError(Exception): def __init__(self, message: str, status_code: int = 400): self.message = message self.status_code = status_code @app.errorhandler(APIError) def handle_api_error(error: APIError): return jsonify({"error": error.message}), error.status_code # Usage in routes: @app.route("/users/<int:user_id>") def get_user(user_id): user = db.get(user_id) if not user: raise APIError("User not found", 404) return jsonify(user)

Request lifecycle hooks:

python @app.before_request def log_request(): app.logger.info(f"{request.method} {request.path}") @app.after_request def add_cors_headers(response): response.headers["Access-Control-Allow-Origin"] = "*" return response @app.teardown_request def cleanup(exception): # Always runs, even if an exception occurred db.session.remove()

flask.g — request-scoped storage: ``python from flask import g @app.before_request def set_request_id(): g.request_id = str(uuid.uuid4()) @app.after_request def add_request_id_header(response): response.headers["X-Request-ID"] = g.request_id return response

Examples

Catch-all error handler with logging

Catch-all Exception handler ensures no stack traces leak to clients

import traceback
from flask import Flask, jsonify, request
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

class NotFoundError(Exception): pass
class ValidationError(Exception): pass

@app.errorhandler(NotFoundError)
def not_found(e):
    return jsonify({"error": str(e)}), 404

@app.errorhandler(ValidationError)
def validation(e):
    return jsonify({"error": str(e)}), 422

@app.errorhandler(Exception)
def unexpected(e):
    app.logger.error(f"Unhandled exception on {request.path}:
{traceback.format_exc()}")
    return jsonify({"error": "Unexpected error"}), 500

Auth middleware via before_request

before_request auth middleware — set g.user_id for all authenticated routes

from functools import wraps
from flask import g, jsonify, request

EXEMPT_PATHS = {"/health", "/auth/login", "/auth/register"}

@app.before_request
def authenticate():
    if request.path in EXEMPT_PATHS:
        return None   # skip auth for public routes

    token = request.headers.get("Authorization", "").removeprefix("Bearer ")
    if not token:
        return jsonify({"error": "Token required"}), 401

    payload = verify_jwt(token)   # raises on invalid
    if not payload:
        return jsonify({"error": "Invalid token"}), 401

    g.user_id = payload["user_id"]
    g.user_role = payload.get("role", "user")

How well did you understand this?

Next in Flask

Flask-SQLAlchemy

Continue