Skip to main content

Global Exception Handling

Global exception handling in FastAPI allows you to catch specific exception types or HTTP status codes across your entire application. This is primarily managed through the FastAPI application class using the @app.exception_handler() decorator or the exception_handlers constructor parameter.

Register Custom Exception Handlers

You can define custom exception classes and register handlers for them using the @app.exception_handler() decorator. This ensures that whenever that exception is raised anywhere in your FastAPI application, the registered handler will process it and return a response.

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

class UnicornException(Exception):
def __init__(self, name: str):
self.name = name

app = FastAPI()

@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)

@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}

Handle HTTP Status Codes

The @app.exception_handler() decorator also accepts integer status codes. This allows you to provide custom responses for standard HTTP errors like 404 Not Found or 500 Internal Server Error.

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(404)
async def not_found_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=404,
content={"message": "The resource you are looking for does not exist."},
)

Override Default Exception Handlers

FastAPI provides default handlers for HTTPException and RequestValidationError. You can override these by registering your own handlers for these specific classes from fastapi.exceptions.

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": exc.body},
)

Reuse Default Handlers

If you want to extend the default behavior (e.g., for logging) while still returning the standard FastAPI response format, you can import and call the built-in handlers from fastapi.exception_handlers.

from fastapi import FastAPI, HTTPException
from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()

@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"HTTP Error occurred: {exc.detail}")
return await http_exception_handler(request, exc)

@app.exception_handler(RequestValidationError)
async def custom_validation_exception_handler(request, exc):
print(f"Validation Error: {exc.errors()}")
return await request_validation_exception_handler(request, exc)

Register Handlers in the Constructor

For more dynamic setups or when organizing code across multiple modules, you can pass a dictionary of handlers directly to the FastAPI constructor using the exception_handlers parameter.

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

async def custom_handler(request, exc):
return JSONResponse({"error": "custom-logic"}, status_code=500)

app = FastAPI(
exception_handlers={
HTTPException: custom_handler,
RequestValidationError: custom_handler,
}
)

Testing Exception Handlers

When testing your application with TestClient, the client will by default re-raise exceptions that occur during the request. To test your custom exception handlers and verify the returned response, you must initialize the TestClient with raise_server_exceptions=False.

from fastapi.testclient import TestClient

# In your test file
client = TestClient(app, raise_server_exceptions=False)

def test_unicorn_exception():
response = client.get("/unicorns/yolo")
assert response.status_code == 418
assert response.json() == {"message": "Oops! yolo did something. There goes a rainbow..."}

Gotchas

  • Starlette vs FastAPI HTTPException: FastAPI's HTTPException (from fastapi.exceptions) inherits from Starlette's HTTPException. It is often safer to register handlers for StarletteHTTPException to ensure you catch exceptions raised by both FastAPI and underlying Starlette components.
  • Middleware Order: Exception handling is implemented via Starlette's ExceptionMiddleware. If you have custom middleware that raises exceptions, ensure they are positioned such that the ExceptionMiddleware can catch them. FastAPI automatically places ExceptionMiddleware around the application router.