python 38 lines · 8 steps

Custom validation error responses in FastAPI

Override FastAPI's validation handler to return a clean, structured error payload with enum hints.

Explained by highlit
1from enum import Enum
2from typing import Annotated
3 
4from fastapi import FastAPI, Query, Request, status
5from fastapi.exceptions import RequestValidationError
6from fastapi.responses import JSONResponse
7 
8app = FastAPI()
9 
10 
11class OrderStatus(str, Enum):
12 pending = "pending"
13 paid = "paid"
14 shipped = "shipped"
15 cancelled = "cancelled"
16 
17 
18@app.exception_handler(RequestValidationError)
19async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
20 errors = []
21 for err in exc.errors():
22 field = ".".join(str(loc) for loc in err["loc"][1:]) or str(err["loc"][0])
23 detail = {"field": field, "message": err["msg"], "type": err["type"]}
24 if err["type"] == "enum":
25 detail["allowed"] = err.get("ctx", {}).get("expected")
26 errors.append(detail)
27 return JSONResponse(
28 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
29 content={"errors": errors},
30 )
31 
32 
33@app.get("/orders")
34async def list_orders(
35 status: Annotated[OrderStatus | None, Query(description="Filter by order status")] = None,
36 limit: Annotated[int, Query(ge=1, le=100)] = 20,
37):
38 return {"status": status, "limit": limit, "items": []}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A single exception handler lets you reshape every validation error into a consistent client-facing format.
  2. 2String-backed enums let FastAPI both constrain input and expose the allowed values in error messages.
  3. 3Annotated with Query attaches constraints like ge and le so bad input is rejected before your handler runs.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Custom validation error responses in FastAPI — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code