KEY INSIGHT
Clients cannot handle errors they cannot parseΓÇöstandardized error schemas turn vague HTTP status codes into actionable debugging information.
HTTP status codes communicate error categories: 400 for client mistakes, 500 for server failures, 429 for rate limits. These codes enable programmatic error handling, but they lack specificity. A client receiving a 400 status code cannot determine whether the request was malformed JSON, missing a required field, or violating a validation constraint without additional context.
RFC 7807 defines a Problem Details format for HTTP APIs. This standard structure includes a type URI identifying the error category, a title summarizing the issue, detail describing what went wrong, and instance indicating which request triggered the failure. Adopting this format ensures consistent error parsing across all API consumers.
```python
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, ValidationError
import logging
logger = logging.getLogger("api.errors")
class ProblemDetail(BaseModel):
type: str
title: str
status: int
detail: str
instance: str
class ErrorHandler:
@staticmethod
def handle_validation_error(request: Request, exc: ValidationError) -> JSONResponse:
errors = []
for error in exc.errors():
errors.append({
"field": ".".join(str(loc) for loc in error["loc"]),
"message": error["msg"],
"type": error["type"],
})
return JSONResponse(
status_code=422,
content={
"type": "https://api.example.com/errors/validation",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Request validation failed",
"instance": str(request.url),
"errors": errors,
}
)
@staticmethod
def handle_generic_error(request: Request, exc: Exception) -> JSONResponse:
logger.exception("Unhandled exception", exc_info=exc)
return JSONResponse(
status_code=500,
content={
"type": "https://api.example.com/errors/internal",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred",
"instance": str(request.url),
}
)
app = FastAPI()
app.add_exception_handler(ValidationError, ErrorHandler.handle_validation_error)
app.add_exception_handler(Exception, ErrorHandler.handle_generic_error)
```
Validation errors return 422 with field-level detail. Rate limit errors return 429 with `Retry-After` headers. Authentication failures return 401 with `WWW-Authenticate` challenge headers. Each error type follows its own conventions while maintaining the Problem Details structure.
Never expose internal error details (stack traces, database errors) in API responses. Log them server-side for debugging while returning generic messages to clients. Internal details help attackers identify vulnerabilities.