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(fromfastapi.exceptions) inherits from Starlette'sHTTPException. It is often safer to register handlers forStarletteHTTPExceptionto 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 theExceptionMiddlewarecan catch them. FastAPI automatically placesExceptionMiddlewarearound the application router.