HTTPException and Error Shape

RoadmapsPython Backend

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

Recall questions

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

Return to Python Backend Roadmap