FastAPI cookbook · Intermediate

Return RFC 9457 Problem Details from FastAPI

Make every error response — validation, not found, conflicts, crashes — use one consistent JSON shape.

Approach

Register exception handlers for your domain errors, HTTPException and RequestValidationError, all returning application/problem+json with type, title, status, detail and extra fields (like errors for validation). Add a catch-all that logs and returns a generic 500.

Solution

from fastapi import Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

PROBLEM = "application/problem+json"


def problem(status: int, title: str, detail: str | None = None, **extra) -> JSONResponse:
    body = {"type": "about:blank", "title": title, "status": status, **({"detail": detail} if detail else {}), **extra}
    return JSONResponse(body, status_code=status, media_type=PROBLEM)


@app.exception_handler(StarletteHTTPException)
async def http_error(request: Request, exc: StarletteHTTPException):
    return problem(exc.status_code, exc.detail if isinstance(exc.detail, str) else "Error")


@app.exception_handler(RequestValidationError)
async def validation_error(request: Request, exc: RequestValidationError):
    errors = [{"field": ".".join(str(p) for p in e["loc"][1:]), "message": e["msg"]} for e in exc.errors()]
    return problem(422, "Validation failed", errors=errors)


@app.exception_handler(DomainError)
async def domain_error(request: Request, exc: DomainError):
    return problem(exc.status_code, exc.title, str(exc))


@app.exception_handler(Exception)
async def unhandled(request: Request, exc: Exception):
    logger.exception("Unhandled error")
    return problem(500, "Internal Server Error")

Pitfalls

  • Handling FastAPI's HTTPException but not Starlette's (404s for unknown routes come from Starlette).
  • Leaking exception messages from unexpected errors.
  • Changing error shapes between versions without notice.

Variations

  • Include a request_id field in every problem for support.

Further reading: RFC 9457: Problem Details

Reference: https://www.rfc-editor.org/rfc/rfc9457

More FastAPI recipes