Error Handling
FastAPI provides a robust system for managing errors through specialized exception classes and customizable exception handlers. This allows you to return consistent, structured error responses to clients while maintaining clean application logic.
Raising HTTP Exceptions
When you need to return an error response to a client (e.g., for a missing resource or failed authentication), you raise the HTTPException class from fastapi.exceptions.
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
# Raising HTTPException returns a JSON response with the status code and detail
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
The FastAPI HTTPException (which inherits from Starlette's HTTPException) allows you to include custom headers in the response:
raise HTTPException(
status_code=401,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
Custom Exception Handlers
You can define how FastAPI should handle specific exceptions globally using the @app.exception_handler() decorator. This is useful for mapping custom application exceptions to specific HTTP responses.
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 amazing. Go to the stars!"},
)
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}
Overriding Default Handlers
FastAPI registers default handlers for HTTPException and RequestValidationError. You can override these to change the default error format or status codes.
Overriding Request Validation Errors
When a request fails Pydantic validation, FastAPI raises a RequestValidationError. By default, this returns a 422 Unprocessable Entity status code. You can override this to return a 400 Bad Request instead:
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc: RequestValidationError):
# Access the list of errors via exc.errors()
return PlainTextResponse(str(exc.errors()), status_code=400)
Overriding the Default HTTP Exception Handler
If you want to change the JSON structure of all HTTPException responses, you can override the handler for StarletteHTTPException (the base class for FastAPI's HTTPException).
from fastapi import FastAPI
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
Validation Exception Types
FastAPI distinguishes between different types of validation failures using specific classes in fastapi.exceptions:
RequestValidationError: Raised when incoming request data (query params, path params, body, etc.) fails validation.ResponseValidationError: Raised when the data returned by your path operation does not match the definedresponse_model. This typically indicates a server-side bug.WebSocketRequestValidationError: A specialized version for WebSocket connections.
All these classes inherit from ValidationException and provide an errors() method to retrieve the underlying Pydantic error details.
WebSocket Error Handling
For WebSocket connections, you can raise WebSocketException to close the connection with a specific code and reason.
from fastapi import FastAPI, WebSocket, WebSocketException, status
app = FastAPI()
@app.websocket("/items/{item_id}/ws")
async def websocket_endpoint(websocket: WebSocket, item_id: str):
await websocket.accept()
if item_id == "forbidden":
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION, reason="Access denied")
await websocket.send_text(f"Message for item: {item_id}")
FastAPI also provides a default websocket_request_validation_exception_handler that automatically closes the connection with code 1008 (Policy Violation) if the initial WebSocket handshake data fails validation.
Troubleshooting and Gotchas
FastAPI vs Starlette HTTPException
FastAPI's HTTPException is located at fastapi.exceptions.HTTPException. While it inherits from starlette.exceptions.HTTPException, it is optimized for FastAPI's dependency injection and response system. If you are writing custom handlers, catching StarletteHTTPException will catch both, but raising the FastAPI version is recommended within path operations to ensure full compatibility with FastAPI features like custom headers.
Validation Error Imports
Always import RequestValidationError from fastapi.exceptions, not from Pydantic. While FastAPI uses Pydantic for validation, RequestValidationError is a FastAPI-specific wrapper that includes additional context like the original request body.
Response Validation
If you encounter a ResponseValidationError, it means your code is returning data that doesn't match your schema. By default, this results in a 500 Internal Server Error. You can catch this in an exception handler if you want to log the specific validation errors before the server returns the 500 response.