HTTPException and Error Shape
Scenario
Your frontend app receives a `500 Internal Server Error` with an HTML traceback instead of JSON. The frontend crashes trying to parse `response.json()`.
Mental model
Exceptions in Python crash the program. In a web API, you don't want to crash the server; you want to intercept the crash and return a neatly formatted JSON apology (the Error Shape) to the client.
Deep dive
When a client requests a resource that doesn't exist, or lacks permissions, you should raise an `HTTPException`.
FastAPI catches `HTTPException` globally and converts it into a JSON response. The default shape is `{"detail": "Error message"}`.
For robust APIs, you should override the global exception handlers to standardize a strict 'Error Shape'. Every error (validation errors, 404s, 500s) should follow the exact same JSON structure so frontend clients can parse them predictably.
Code examples
Standard HTTPException
from fastapi import HTTPException, status
def get_user(user_id: int):
user = db.query(User).get(user_id)
if not user:
# Halts execution and returns 404 JSON
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return user
You don't `return` an error response. You `raise` it. FastAPI catches it and converts it to `{'detail': 'User not found'}`.
Custom Error Shape (Exception Handler)
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
class CustomError(Exception):
def __init__(self, message: str, code: str):
self.message = message
self.code = code
@app.exception_handler(CustomError)
async def custom_error_handler(request: Request, exc: CustomError):
return JSONResponse(
status_code=400,
content={"error": {"code": exc.code, "message": exc.message}},
)
This guarantees the frontend always gets `{'error': {'code': '...', 'message': '...'}}` regardless of where `CustomError` was raised.
Common mistakes
- Leaking stack traces in production: If an unhandled exception occurs (e.g., `ZeroDivisionError`), it becomes a 500. Never expose the raw stack trace in the JSON payload in production, as it leaks internal system paths and logic.
Recall questions
- What happens when you `raise HTTPException(...)` in FastAPI?
- What is the default JSON shape of a FastAPI `HTTPException`?
- How do you catch custom domain exceptions and convert them to standard JSON errors?
Questions & answers
Why is it better to raise exceptions for missing resources rather than returning `None` up to the router?
Raising an exception halts the flow immediately (Fail Fast). If you return `None`, every function up the call stack has to check `if result is None:` and handle it, creating massive boilerplate.
How do you ensure the frontend doesn't crash when your API hits a 500 Internal Server Error?
I register a global exception handler for the base `Exception` class. It logs the full traceback internally, but returns a clean, standardized JSON response (e.g. `{'error': 'Internal Server Error'}`) with a 500 status code.
Continue learning
Previous: Status Codes & OpenAPI