FastAPI validates inputs automatically and raises HTTPException for deliberate errors. Learning the error flow is key to building APIs that fail clearly instead of crashing.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
return items_db[item_id]
Raising HTTPException(404, "Item not found") produces the response {"detail": "Item not found"} with status 404. detail can be any JSON-serializable value, including a dict or list.
raise HTTPException(
status_code=403,
detail="Not enough permissions",
headers={"WWW-Authenticate": "Bearer"},
)
When request data does not match the declared types/constraints, FastAPI returns 422 before your function runs:
{
"detail": [
{
"type": "greater_than",
"loc": ["path", "item_id"],
"msg": "Input should be greater than 0",
"input": "-5"
}
]
}
loc tells you where the problem is: path, query, body (nested as body.field.subfield).
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content={"errors": exc.errors(), "body": exc.body.decode() if isinstance(exc.body, bytes) else exc.body},
)
class UnicornException(Exception):
pass
@app.exception_handler(UnicornException)
async def unicorn_handler(request: Request, exc: UnicornException):
return JSONResponse(status_code=418, content={"message": f"Oops: {exc}"})
RequestValidationError, StarletteHTTPException) let you reshape the default error format (useful to match an error contract like RFC 7807 "problem details").from pydantic import BaseModel, field_validator
class Order(BaseModel):
quantity: int
price: float
@field_validator("quantity")
@classmethod
def quantity_positive(cls, v: int) -> int:
if v <= 0:
raise ValueError("quantity must be positive")
return v
Use model validators for data-shaped rules (formats, ranges, cross-field checks); use HTTPException inside endpoints for resource-state rules (does the item exist?, is the user allowed?).
A: Always raise. Returning {"error": ...} with status 200 breaks clients that check the status code and pollutes the docs. Exceptions also stop execution immediately.
A: Override the RequestValidationError handler and return JSONResponse(status_code=400, ...) instead of 422, or use FastAPI(exception_handlers={...}) at app creation.
A: They bubble up to the server, which returns a generic 500 and logs the traceback. Catch what you can meaningfully handle; let programming bugs crash loudly in dev, and add monitoring (Sentry, OpenTelemetry) in production.
FastAPI's error model has three layers: automatic 422 validation errors for malformed input, HTTPException for expected application errors, and custom exception handlers for shaping responses. Together they give clients precise, consistent failure information.