Skip to content

FastAPI Custom Exception Fix

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about FastAPI Custom Exception Fix. We cover key concepts, practical examples, and best practices.

The Problem

Default 400/404/500 errors lack context. Your API consumers need structured error responses with error codes, details, and hints — not just a generic message.

Quick Fix

Wrong — raising generic HTTPException

from fastapi import HTTPException

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    item = await fetch_item(item_id)
    if not item:
        raise HTTPException(status_code=404, detail="Not found")
    if item.price > 10000:
        raise HTTPException(status_code=400, detail="Too expensive")
    return item

Output: Generic error responses. No error codes, no hints, inconsistent format.

Correct — custom exception class

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

class AppException(Exception):
    def __init__(self, error_code: str, message: str, status_code: int = 400, details: dict = None):
        self.error_code = error_code
        self.message = message
        self.status_code = status_code
        self.details = details or {}

app = FastAPI()

@app.exception_handler(AppException)
async def app_exception_handler(request: Request, exc: AppException):
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": exc.error_code,
            "message": exc.message,
            "details": exc.details,
        },
    )

Using the custom exception

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    item = await fetch_item(item_id)
    if not item:
        raise AppException(
            error_code="ITEM_NOT_FOUND",
            message=f"Item {item_id} not found",
            status_code=404,
        )
    if item.price > 10000:
        raise AppException(
            error_code="ITEM_TOO_EXPENSIVE",
            message="This item exceeds the price limit",
            details={"max_price": 10000, "item_price": item.price},
        )
    return item

Output:

{
  "error": "ITEM_NOT_FOUND",
  "message": "Item 42 not found",
  "details": {}
}

Validation error customization

from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=422,
        content={
            "error": "VALIDATION_ERROR",
            "message": "Invalid request data",
            "details": exc.errors(),
        },
    )

Exception hierarchy

class NotFoundException(AppException):
    def __init__(self, resource: str, resource_id):
        super().__init__(
            error_code="NOT_FOUND",
            message=f"{resource} {resource_id} not found",
            status_code=404,
        )

class UnauthorizedException(AppException):
    def __init__(self):
        super().__init__(
            error_code="UNAUTHORIZED",
            message="Authentication required",
            status_code=401,
        )

Prevention

  • Create a custom exception class for structured error responses.
  • Register exception handlers for your custom exceptions and built-in ones.
  • Use consistent error codes that clients can parse programmatically.

Common Mistakes with custom exception

  1. Using foldl instead of foldl' causing stack overflow on large lists
  2. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  3. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable

These mistakes appear frequently in real-world FASTAPI code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What's the difference between HTTPException and custom exceptions?

HTTPException returns FastAPI's default error format. Custom exceptions let you control the response structure, error codes, and additional context.

Can I return errors without exceptions?

Yes, you can return JSONResponse(status_code=400, content={...}) directly. Exceptions are cleaner for reusable error patterns.

How do I handle unhandled exceptions globally?

Register a handler for Exception (the base class) to catch everything. Be careful not to swallow debugging information.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro