App-Level Exception Handling
To catch and process specific exceptions or HTTP status codes globally in your application, use the @app.exception_handler() decorator on your FastAPI instance.
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}
How Exception Handlers Work
When you use @app.exception_handler(exc_class_or_status_code), you register a custom function to handle a specific type of error.
- Arguments: The handler function must be
asyncand accept two arguments: aRequestobject and the exception instance (or the status code if handling anint). - Return Value: The function must return a
Response(such asJSONResponse,HTMLResponse, or a plainResponse). - Registration: This decorator calls
self.add_exception_handlerinternally to update theexception_handlersdictionary in theFastAPIclass.
Handling HTTP Status Codes
You can register handlers for specific HTTP status codes instead of exception classes. This is useful for customizing standard error pages like 404 Not Found.
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."},
)
Overriding Default Handlers
FastAPI provides default handlers for HTTPException and RequestValidationError. You can override these to customize the response format while still utilizing the original logic if needed.
from fastapi import FastAPI
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"Logging HTTP error: {exc.detail}")
# Call the original default handler to return the standard response
return await http_exception_handler(request, exc)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
# Return a custom structure for validation errors
return JSONResponse(
status_code=422,
content={"custom_error_list": exc.errors(), "message": "Invalid data sent"},
)
Bulk Registration via Constructor
If you prefer to define your handlers elsewhere, you can pass them as a dictionary to the FastAPI constructor using the exception_handlers parameter.
from fastapi import FastAPI
from my_app.exceptions import UnicornException, unicorn_handler
# Map exception classes or status codes to their handler functions
handlers = {
UnicornException: unicorn_handler,
404: not_found_handler
}
app = FastAPI(exception_handlers=handlers)
Troubleshooting and Gotchas
FastAPI vs Starlette HTTPException
FastAPI has its own HTTPException (in fastapi.exceptions) which inherits from Starlette's HTTPException. The FastAPI version allows you to add custom headers. When catching HTTP exceptions:
- If you catch
starlette.exceptions.HTTPException, it will catch both Starlette and FastAPI HTTP exceptions. - If you catch
fastapi.exceptions.HTTPException, it will only catch the FastAPI version.
Handler Inheritance
Handlers follow standard Python inheritance. If you register a handler for the base Exception class, it will catch all exceptions that do not have a more specific handler registered.
Re-using Default Logic
The default handlers are available in fastapi.exception_handlers:
http_exception_handler: HandlesStarletteHTTPException.request_validation_exception_handler: Handles Pydantic validation errors (RequestValidationError).websocket_request_validation_exception_handler: Handles validation errors in WebSockets.