FastAPI Error Handling

When something goes wrong in your API — a resource does not exist, a user has no permission, or input fails a business rule — you raise an exception. FastAPI converts it into a clean JSON error response with the right HTTP status code. This topic shows you how to use built-in exceptions and create your own.

The HTTPException — Your Main Tool

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/users/{user_id}")
def get_user(user_id: int):
    if user_id != 1:
        raise HTTPException(status_code=404, detail="User not found")
    return {"id": 1, "name": "Meera"}
GET /users/1
→ 200 OK
→ {"id": 1, "name": "Meera"}

GET /users/99
→ 404 Not Found
→ {"detail": "User not found"}

Common HTTP Error Codes to Use

Status Code    When to raise it
────────────────────────────────────────────────────
400            Bad request — client sent invalid data
401            Unauthorized — not logged in
403            Forbidden — logged in but no permission
404            Not found — resource does not exist
409            Conflict — duplicate entry
422            Unprocessable entity (FastAPI auto-raises)
500            Server error — something broke on your side

Adding Extra Headers to an Error

Some errors require extra headers — like a 401 that tells the client which auth method to use:

@app.get("/secure")
def secure_route(token: str):
    if token != "valid-token":
        raise HTTPException(
            status_code=401,
            detail="Invalid token",
            headers={"WWW-Authenticate": "Bearer"}
        )
    return {"access": "granted"}

Custom Exception Classes

For business-specific errors that appear in many places, create a custom exception class:

class InsufficientBalanceError(Exception):
    def __init__(self, balance: float, required: float):
        self.balance = balance
        self.required = required

Registering a Custom Exception Handler

Tell FastAPI how to convert your custom exception into an HTTP response:

from fastapi import Request
from fastapi.responses import JSONResponse

@app.exception_handler(InsufficientBalanceError)
async def balance_exception_handler(request: Request, exc: InsufficientBalanceError):
    return JSONResponse(
        status_code=402,
        content={
            "error": "Insufficient balance",
            "your_balance": exc.balance,
            "amount_needed": exc.required
        }
    )
Now when any route raises InsufficientBalanceError:

raise InsufficientBalanceError(balance=50.0, required=200.0)

→ 402 Payment Required
→ {
    "error": "Insufficient balance",
    "your_balance": 50.0,
    "amount_needed": 200.0
  }

Overriding the Default Validation Error Handler

FastAPI raises RequestValidationError when input fails Pydantic validation. You can override how this error looks:

from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def validation_error_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=400,
        content={
            "message": "Invalid input",
            "errors": exc.errors()
        }
    )

Using try/except Inside a Route

For errors from external code (databases, third-party libraries), catch them inside the route and convert to HTTPException:

@app.get("/data")
def get_data():
    try:
        result = external_service.fetch()
    except ConnectionError:
        raise HTTPException(
            status_code=503,
            detail="External service unavailable. Try again later."
        )
    return result

Error Handling Flow

Route function raises exception
         │
         ▼
Is there a registered exception_handler?
    Yes → run handler → return custom response
    No  → is it HTTPException?
            Yes → FastAPI converts to JSON error
            No  → 500 Internal Server Error

Key Points

  • Use HTTPException with a status_code and detail for standard errors.
  • Create custom exception classes for domain-specific errors.
  • Register handlers with @app.exception_handler() to control the response format.
  • Override RequestValidationError to customize validation error responses.
  • Always catch external library errors and convert them to HTTPException before they reach the client.

Leave a Comment

Your email address will not be published. Required fields are marked *