- Spec files
pypi-fastapi
Describes: pkg:pypi/fastapi@0.116.x
- Description
- FastAPI framework, high performance, easy to learn, fast to code, ready for production
- Author
- tessl
- Last updated
exception-handling.md docs/
1# Exception Handling23FastAPI provides comprehensive exception handling capabilities that integrate with HTTP status codes, automatic error responses, and custom exception handlers. The framework includes built-in exceptions for common scenarios and allows for custom exception handling patterns.45## Capabilities67### HTTP Exception89The primary exception class for handling HTTP errors with proper status codes and response formatting.1011```python { .api }12class HTTPException(Exception):13def __init__(14self,15status_code: int,16detail: Any = None,17headers: Dict[str, str] = None18) -> None:19"""20HTTP exception with status code and detail message.2122Parameters:23- status_code: HTTP status code (400, 401, 404, 500, etc.)24- detail: Error detail message or structured data25- headers: Additional HTTP headers to include in error response26"""27self.status_code = status_code28self.detail = detail29self.headers = headers30```3132### WebSocket Exception3334Exception class specifically for WebSocket connection errors.3536```python { .api }37class WebSocketException(Exception):38def __init__(39self,40code: int,41reason: str = None42) -> None:43"""44WebSocket error exception.4546Parameters:47- code: WebSocket close code (1000-4999)48- reason: Human-readable close reason49"""50self.code = code51self.reason = reason52```5354### FastAPI Error5556Generic runtime error exception for FastAPI framework issues.5758```python { .api }59class FastAPIError(Exception):60def __init__(self, message: str = None) -> None:61"""62Generic FastAPI runtime error.6364Parameters:65- message: Error message describing the issue66"""67self.message = message68```6970### Validation Exceptions7172Exception classes for handling request and response validation errors with detailed error information.7374```python { .api }75class ValidationException(Exception):76def __init__(self, errors: List[dict]) -> None:77"""78Base validation error exception.7980Parameters:81- errors: List of validation error dictionaries82"""83self.errors = errors8485class RequestValidationError(ValidationException):86def __init__(self, errors: List[ErrorWrapper]) -> None:87"""88Request validation error with detailed error information.8990Parameters:91- errors: List of Pydantic ErrorWrapper objects containing92field-specific validation error details93"""94self.errors = errors95self.body = getattr(errors, "body", None)9697class WebSocketRequestValidationError(ValidationException):98def __init__(self, errors: List[ErrorWrapper]) -> None:99"""100WebSocket request validation error.101102Parameters:103- errors: List of Pydantic ErrorWrapper objects for WebSocket104parameter validation failures105"""106self.errors = errors107108class ResponseValidationError(ValidationException):109def __init__(self, errors: List[ErrorWrapper]) -> None:110"""111Response validation error for response model validation failures.112113Parameters:114- errors: List of Pydantic ErrorWrapper objects containing115response validation error details116"""117self.errors = errors118```119120### Exception Handler Decorator121122Decorator method for registering custom exception handlers on FastAPI and APIRouter instances.123124```python { .api }125def exception_handler(126self,127exc_class_or_status_code: Union[int, Type[Exception]]128) -> Callable[[Callable], Callable]:129"""130Decorator for adding exception handlers.131132Parameters:133- exc_class_or_status_code: Exception class or HTTP status code to handle134135Returns:136Decorator function for exception handler registration137"""138```139140### Exception Handler Function Type141142Type signature for custom exception handler functions.143144```python { .api }145async def exception_handler_function(146request: Request,147exc: Exception148) -> Response:149"""150Exception handler function signature.151152Parameters:153- request: HTTP request object154- exc: Exception instance that was raised155156Returns:157Response object to send to client158"""159```160161## Usage Examples162163### Basic HTTP Exception Handling164165```python166from fastapi import FastAPI, HTTPException, status167168app = FastAPI()169170@app.get("/items/{item_id}")171async def read_item(item_id: int):172if item_id == 0:173raise HTTPException(174status_code=status.HTTP_404_NOT_FOUND,175detail="Item not found"176)177if item_id < 0:178raise HTTPException(179status_code=status.HTTP_400_BAD_REQUEST,180detail="Item ID must be positive",181headers={"X-Error": "Invalid item ID"}182)183return {"item_id": item_id, "name": f"Item {item_id}"}184```185186### Custom Exception Handler187188```python189from fastapi import FastAPI, Request, HTTPException190from fastapi.responses import JSONResponse191from fastapi.exceptions import RequestValidationError192from starlette.exceptions import HTTPException as StarletteHTTPException193194app = FastAPI()195196@app.exception_handler(StarletteHTTPException)197async def http_exception_handler(request: Request, exc: StarletteHTTPException):198return JSONResponse(199status_code=exc.status_code,200content={201"error": "HTTP Exception",202"message": exc.detail,203"path": request.url.path,204"method": request.method205}206)207208@app.exception_handler(RequestValidationError)209async def validation_exception_handler(request: Request, exc: RequestValidationError):210return JSONResponse(211status_code=422,212content={213"error": "Validation Error",214"message": "Invalid request data",215"details": exc.errors(),216"body": exc.body217}218)219220@app.get("/items/{item_id}")221async def read_item(item_id: int):222if item_id == 42:223raise HTTPException(status_code=418, detail="I'm a teapot")224return {"item_id": item_id}225```226227### Custom Exception Classes228229```python230from fastapi import FastAPI, Request, HTTPException231from fastapi.responses import JSONResponse232233app = FastAPI()234235# Custom exception classes236class ItemNotFoundError(Exception):237def __init__(self, item_id: int):238self.item_id = item_id239self.message = f"Item with ID {item_id} not found"240super().__init__(self.message)241242class InsufficientPermissionsError(Exception):243def __init__(self, required_role: str, user_role: str):244self.required_role = required_role245self.user_role = user_role246self.message = f"Required role: {required_role}, user role: {user_role}"247super().__init__(self.message)248249class BusinessLogicError(Exception):250def __init__(self, message: str, error_code: str):251self.message = message252self.error_code = error_code253super().__init__(self.message)254255# Exception handlers256@app.exception_handler(ItemNotFoundError)257async def item_not_found_handler(request: Request, exc: ItemNotFoundError):258return JSONResponse(259status_code=404,260content={261"error": "Item Not Found",262"message": exc.message,263"item_id": exc.item_id264}265)266267@app.exception_handler(InsufficientPermissionsError)268async def insufficient_permissions_handler(request: Request, exc: InsufficientPermissionsError):269return JSONResponse(270status_code=403,271content={272"error": "Insufficient Permissions",273"message": exc.message,274"required_role": exc.required_role,275"user_role": exc.user_role276}277)278279@app.exception_handler(BusinessLogicError)280async def business_logic_handler(request: Request, exc: BusinessLogicError):281return JSONResponse(282status_code=422,283content={284"error": "Business Logic Error",285"message": exc.message,286"error_code": exc.error_code287}288)289290# Routes using custom exceptions291@app.get("/items/{item_id}")292async def get_item(item_id: int):293if item_id == 999:294raise ItemNotFoundError(item_id)295return {"item_id": item_id, "name": f"Item {item_id}"}296297@app.delete("/items/{item_id}")298async def delete_item(item_id: int, user_role: str = "user"):299if user_role != "admin":300raise InsufficientPermissionsError("admin", user_role)301if item_id <= 0:302raise BusinessLogicError("Cannot delete items with non-positive IDs", "INVALID_ID")303return {"message": f"Item {item_id} deleted"}304```305306### Exception Handling with Logging307308```python309import logging310from datetime import datetime311from fastapi import FastAPI, Request, HTTPException312from fastapi.responses import JSONResponse313314# Configure logging315logging.basicConfig(level=logging.INFO)316logger = logging.getLogger(__name__)317318app = FastAPI()319320@app.exception_handler(Exception)321async def global_exception_handler(request: Request, exc: Exception):322# Log the exception323logger.error(324f"Unhandled exception: {type(exc).__name__}: {str(exc)}",325extra={326"path": request.url.path,327"method": request.method,328"timestamp": datetime.utcnow().isoformat()329}330)331332# Return generic error response333return JSONResponse(334status_code=500,335content={336"error": "Internal Server Error",337"message": "An unexpected error occurred",338"timestamp": datetime.utcnow().isoformat()339}340)341342@app.exception_handler(HTTPException)343async def http_exception_handler(request: Request, exc: HTTPException):344# Log HTTP exceptions345logger.warning(346f"HTTP {exc.status_code}: {exc.detail}",347extra={348"path": request.url.path,349"method": request.method,350"status_code": exc.status_code351}352)353354return JSONResponse(355status_code=exc.status_code,356content={357"error": f"HTTP {exc.status_code}",358"message": exc.detail,359"timestamp": datetime.utcnow().isoformat()360},361headers=exc.headers362)363364@app.get("/error-demo/{error_type}")365async def error_demo(error_type: str):366if error_type == "404":367raise HTTPException(status_code=404, detail="Resource not found")368elif error_type == "500":369raise Exception("Simulated internal server error")370elif error_type == "400":371raise HTTPException(status_code=400, detail="Bad request")372return {"message": "No error"}373```374375### Validation Error Customization376377```python378from fastapi import FastAPI, Request379from fastapi.exceptions import RequestValidationError380from fastapi.responses import JSONResponse381from pydantic import BaseModel, validator382383app = FastAPI()384385class ItemModel(BaseModel):386name: str387price: float388description: str = None389390@validator('price')391def price_must_be_positive(cls, v):392if v <= 0:393raise ValueError('Price must be positive')394return v395396@validator('name')397def name_must_not_be_empty(cls, v):398if not v.strip():399raise ValueError('Name cannot be empty')400return v401402@app.exception_handler(RequestValidationError)403async def validation_exception_handler(request: Request, exc: RequestValidationError):404errors = []405for error in exc.errors():406field_path = " -> ".join(str(loc) for loc in error["loc"])407errors.append({408"field": field_path,409"message": error["msg"],410"type": error["type"],411"input": error.get("input")412})413414return JSONResponse(415status_code=422,416content={417"error": "Validation Failed",418"message": "The request contains invalid data",419"errors": errors,420"total_errors": len(errors)421}422)423424@app.post("/items/")425async def create_item(item: ItemModel):426return {"message": "Item created", "item": item}427```428429### WebSocket Exception Handling430431```python432from fastapi import FastAPI, WebSocket, WebSocketDisconnect433from fastapi.exceptions import WebSocketException434435app = FastAPI()436437@app.websocket("/ws")438async def websocket_endpoint(websocket: WebSocket):439await websocket.accept()440try:441while True:442data = await websocket.receive_text()443444# Validate WebSocket data445if not data.strip():446raise WebSocketException(447code=1003,448reason="Empty message not allowed"449)450451if len(data) > 1000:452raise WebSocketException(453code=1009,454reason="Message too large"455)456457# Echo the message back458await websocket.send_text(f"Echo: {data}")459460except WebSocketDisconnect:461print("WebSocket client disconnected")462except WebSocketException as e:463print(f"WebSocket error {e.code}: {e.reason}")464await websocket.close(code=e.code, reason=e.reason)465except Exception as e:466print(f"Unexpected WebSocket error: {e}")467await websocket.close(code=1011, reason="Internal server error")468```469470### Exception Handling in Dependencies471472```python473from fastapi import FastAPI, Depends, HTTPException, Header474475app = FastAPI()476477def verify_auth_header(authorization: str = Header(None)):478if not authorization:479raise HTTPException(480status_code=401,481detail="Authorization header missing",482headers={"WWW-Authenticate": "Bearer"}483)484485if not authorization.startswith("Bearer "):486raise HTTPException(487status_code=401,488detail="Invalid authorization format",489headers={"WWW-Authenticate": "Bearer"}490)491492token = authorization.replace("Bearer ", "")493if token != "valid-token":494raise HTTPException(495status_code=401,496detail="Invalid token",497headers={"WWW-Authenticate": "Bearer"}498)499500return token501502def get_user_role(token: str = Depends(verify_auth_header)):503# Simulate role extraction from token504if token == "valid-token":505return "admin"506return "user"507508def require_admin_role(role: str = Depends(get_user_role)):509if role != "admin":510raise HTTPException(511status_code=403,512detail="Admin role required"513)514return role515516@app.get("/admin/users")517async def list_users(role: str = Depends(require_admin_role)):518return {"users": ["user1", "user2"], "requesting_role": role}519520@app.get("/profile")521async def get_profile(token: str = Depends(verify_auth_header)):522return {"message": "Profile data", "token": token}523```524525### Context Manager for Exception Handling526527```python528from contextlib import asynccontextmanager529from fastapi import FastAPI, HTTPException530import asyncio531532app = FastAPI()533534@asynccontextmanager535async def handle_database_errors():536try:537yield538except ConnectionError:539raise HTTPException(540status_code=503,541detail="Database connection failed"542)543except TimeoutError:544raise HTTPException(545status_code=504,546detail="Database operation timed out"547)548except Exception as e:549raise HTTPException(550status_code=500,551detail=f"Database error: {str(e)}"552)553554@app.get("/users/{user_id}")555async def get_user(user_id: int):556async with handle_database_errors():557# Simulate database operations558if user_id == 1:559raise ConnectionError("DB connection lost")560elif user_id == 2:561await asyncio.sleep(10) # Simulate timeout562raise TimeoutError("Operation timed out")563elif user_id == 3:564raise Exception("Unknown database error")565566return {"user_id": user_id, "name": f"User {user_id}"}567```