FastAPI Custom Exception Fix
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
- Using
foldlinstead offoldl'causing stack overflow on large lists - Forgetting
deriving (Show, Eq)on custom data types needed for debugging - 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro