Skip to main content

Error Handling and Exceptions

This project manages application errors and validation failures through a hierarchy of specialized exception classes and a flexible handler system. By distinguishing between client-side errors, server-side validation failures, and custom domain exceptions, the codebase ensures that errors are both informative for developers and correctly formatted for clients.

Core Exception Classes

The primary mechanism for signaling errors in path operations is the HTTPException class, located in fastapi/exceptions.py.

HTTPException

While it inherits from Starlette's HTTPException, the FastAPI version is enhanced to support any JSON-serializable object in its detail field. This allows for structured error responses beyond simple strings.

from fastapi import HTTPException

# Raising a simple error
raise HTTPException(status_code=404, detail="Item not found")

# Raising a structured error
raise HTTPException(
status_code=400,
detail={"error_code": "INVALID_PAYMENT", "message": "Insufficient funds"}
)

WebSocketException

For WebSocket connections, WebSocketException (also in fastapi/exceptions.py) allows closing connections with specific codes and reasons, adhering to the WebSocket protocol.

from fastapi import WebSocketException, status

raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION, reason="Invalid session")

Validation Errors

Validation errors occur when data fails to match the Pydantic schemas defined for request parameters or response models. These exceptions inherit from a base ValidationException which includes logic to capture the EndpointContext (file, line, and function name) where the error occurred.

RequestValidationError

Raised when the client sends invalid data (e.g., missing fields, wrong types). By default, this results in a 422 Unprocessable Entity response. It includes the raw body that failed validation to assist in debugging.

ResponseValidationError

Raised when the data returned by a path operation fails to validate against the response_model. Note: Unlike request validation, a ResponseValidationError results in a 500 Internal Server Error. This is because a response validation failure indicates a bug in the server-side code rather than a mistake by the client.

Endpoint Context in Exceptions

The ValidationException class in fastapi/exceptions.py provides a formatted string representation that includes the specific location in the source code where the validation failed:

# Internal representation in fastapi/exceptions.py
def _format_endpoint_context(self) -> str:
if not (self.endpoint_file and self.endpoint_line and self.endpoint_function):
return ""
return f'\n File "{self.endpoint_file}", line {self.endpoint_line}, in {self.endpoint_function}'

Custom Exception Handlers

You can define how specific exceptions should be handled globally using the @app.exception_handler decorator. This decouples business logic from HTTP response formatting.

As seen in docs_src/handling_errors/tutorial003_py310.py, you can register a handler for a custom exception:

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

@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 wrong."},
)

Reusing Default Handlers

The project provides default handlers in fastapi/exception_handlers.py that can be imported and reused or extended. This is useful when you want to perform an action (like logging) before falling back to the standard FastAPI error response.

Available default handlers include:

  • http_exception_handler: Handles HTTPException by returning a JSONResponse.
  • request_validation_exception_handler: Handles RequestValidationError by returning a 422 response with error details.
  • websocket_request_validation_exception_handler: Closes WebSockets with a policy violation code.

Example of extending a default handler:

from fastapi.exception_handlers import http_exception_handler
from starlette.exceptions import HTTPException as StarletteHTTPException

@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"Logging an error: {exc.detail}")
return await http_exception_handler(request, exc)

When overriding handlers, it is often recommended to catch the Starlette version of HTTPException to ensure both FastAPI-raised and Starlette-raised exceptions are captured.