Handling Validation Errors
FastAPI provides a structured way to handle data validation failures through a hierarchy of exception classes. These exceptions distinguish between errors caused by the client (invalid requests) and errors caused by the server (invalid responses), while providing detailed context for debugging.
Request Validation Errors
When a client sends data that does not match the schema defined in your path operation (e.g., a missing required field in a JSON body or an invalid type in a query parameter), FastAPI raises a fastapi.exceptions.RequestValidationError.
By default, this exception results in an HTTP 422 Unprocessable Entity response. The default handler, request_validation_exception_handler in fastapi/exception_handlers.py, returns a JSON response containing the error details:
async def request_validation_exception_handler(
request: Request, exc: RequestValidationError
) -> JSONResponse:
return JSONResponse(
status_code=422,
content={"detail": jsonable_encoder(exc.errors())},
)
Accessing the Request Body
The RequestValidationError class includes a body attribute containing the data that failed validation. This is particularly useful for logging the exact input that caused the error.
from fastapi import FastAPI, Request, status
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=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"detail": exc.errors(),
"body": exc.body, # Include the original body in the response
},
)
Response Validation Errors
If your path operation function returns data that does not match the defined response_model, FastAPI raises a fastapi.exceptions.ResponseValidationError.
Unlike request errors, this is considered a server-side error (HTTP 500) because it indicates that the application code is producing data that violates its own contract. FastAPI does not provide a default JSON handler for this exception in fastapi/exception_handlers.py; instead, it is allowed to bubble up, typically resulting in a 500 Internal Server Error handled by the server's global exception middleware.
In fastapi/routing.py, the serialize_response function validates the return value and raises the error if validation fails:
# Internal logic in fastapi/routing.py
if errors:
ctx = endpoint_ctx or EndpointContext()
raise ResponseValidationError(
errors=errors,
body=response_content,
endpoint_ctx=ctx,
)
The ValidationException Base Class
Both RequestValidationError and ResponseValidationError inherit from fastapi.exceptions.ValidationException. This base class is designed to capture the "Endpoint Context"—the specific location in your code where the validation failed.
Endpoint Context and Debugging
When an exception is raised, FastAPI uses inspect to extract the file path, line number, and function name of the path operation. This information is stored in the endpoint_ctx attribute.
The ValidationException class implements a __str__ method that formats these details into a readable message for server logs:
# From fastapi/exceptions.py
def _format_endpoint_context(self) -> str:
if not (self.endpoint_file and self.endpoint_line and self.endpoint_function):
if self.endpoint_path:
return f"\n Endpoint: {self.endpoint_path}"
return ""
context = f'\n File "{self.endpoint_file}", line {self.endpoint_line}, in {self.endpoint_function}'
if self.endpoint_path:
context += f"\n {self.endpoint_path}"
return context
When you see a validation error in your terminal, it will look like this:
1 validation error:
body -> item_id
field required (type=value_error.missing)
File "/app/main.py", line 15, in read_item
GET /items/{item_id}
Customizing Validation Handlers
You can override how FastAPI handles these errors using the @app.exception_handler decorator. This allows you to change the status code, format the JSON response, or add custom logging.
Wrapping Default Handlers
If you want to perform an action (like logging) but keep the default FastAPI response behavior, you can import and call the built-in handlers:
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.exception_handlers import request_validation_exception_handler
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def custom_validation_handler(request, exc):
print(f"Validation failed for {request.url}: {exc}")
return await request_validation_exception_handler(request, exc)
WebSocket Validation Errors
For WebSocket connections, FastAPI uses fastapi.exceptions.WebSocketRequestValidationError. If validation fails during the dependency resolution or initial handshake of a WebSocket, the default handler websocket_request_validation_exception_handler closes the connection with a WS_1008_POLICY_VIOLATION code:
# From fastapi/exception_handlers.py
async def websocket_request_validation_exception_handler(
websocket: WebSocket, exc: WebSocketRequestValidationError
) -> None:
await websocket.close(
code=WS_1008_POLICY_VIOLATION, reason=jsonable_encoder(exc.errors())
)