Handle RequestValidationError Globally in FastAPI

This article provides a straightforward guide to handling FastAPI's RequestValidationError at a global level. By registering a custom exception handler for this specific error, you can intercept invalid incoming payload or query data, customize the default HTTP 422 response body, and maintain a consistent error structure across your entire API application.

Understanding RequestValidationError

When an incoming HTTP request contains data that does not conform to your Pydantic schemas, FastAPI automatically raises a RequestValidationError. By default, FastAPI catches this exception and returns an HTTP 422 Unprocessable Entity status code with a detailed list of validation errors. In production applications, you often need to customize this response format to match your API's standard error envelope.

The Registration Procedure

To register a global handler for RequestValidationError, follow these steps:

1. Import Necessary Modules

You must import RequestValidationError from fastapi.exceptions, Request and status from fastapi, and JSONResponse from fastapi.responses.

from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel

2. Define the Custom Exception Handler

Create an asynchronous function that accepts two parameters: the incoming Request object and the caught RequestValidationError instance. Inside the function, you can inspect exc.errors() for validation details or exc.body to see the raw request data. Construct and return a JSONResponse with your desired structure.

async def validation_exception_handler(request: Request, exc: RequestValidationError):
    # Extract details from the exception
    errors = exc.errors()
    
    # Structure a custom response body
    custom_response = {
        "success": False,
        "message": "Validation failed for the request payload.",
        "errors": [
            {
                "field": " -> ".join(str(loc) for loc in err.get("loc", [])),
                "issue": err.get("msg"),
                "type": err.get("type")
            }
            for err in errors
        ]
    }
    
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=custom_response
    )

3. Register the Handler with the Application

You can register the handler to your FastAPI instance using either the decorator syntax or the add_exception_handler method.

Option A: Using the Decorator

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def custom_validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content={"detail": "Custom validation error message", "errors": exc.errors()}
    )

Option B: Using add_exception_handler

app = FastAPI()

app.add_exception_handler(RequestValidationError, validation_exception_handler)

Complete Implementation Example

Below is a complete, working example illustrating the registration and behavior of the global handler:

from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field

app = FastAPI()

# Register global exception handler
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content={
            "status": "error",
            "error_code": "INVALID_INPUT",
            "details": exc.errors()
        }
    )

# Sample schema
class UserItem(BaseModel):
    name: str = Field(..., min_length=3)
    age: int = Field(..., ge=18)

# Sample endpoint
@app.post("/users/")
async def create_user(user: UserItem):
    return {"message": "User created successfully", "user": user}

Whenever a client sends invalid data to /users/—such as an age below 18 or a missing name—FastAPI intercepts the failure and routes it through your custom global handler, returning the formatted JSON structure instead of the default FastAPI validation error format.