← Back to Python

All Topics

Advertisement

Learn/Python/API Development

Error Handling - Custom Exceptions, Error Responses

Topic: Error Handling

Advertisement

Introduction

Proper error handling ensures robust applications with clear error messages. This tutorial covers custom exceptions, standardized error responses, and error handling in Flask and FastAPI.

Custom Exceptions

# exceptions.py
class AppException(Exception):
    """Base exception for application errors."""
    def __init__(self, message: str, status_code: int = 500, details: dict = None):
        self.message = message
        self.status_code = status_code
        self.details = details
        super().__init__(self.message)

class NotFoundError(AppException):
    """Resource not found."""
    def __init__(self, resource: str, identifier: str):
        super().__init__(
            message=f"{resource} not found",
            status_code=404,
            details={"resource": resource, "identifier": identifier}
        )

class ValidationError(AppException):
    """Validation error."""
    def __init__(self, message: str, errors: list = None):
        super().__init__(
            message=message,
            status_code=400,
            details={"errors": errors or []}
        )

class AuthenticationError(AppException):
    """Authentication failed."""
    def __init__(self, message: str = "Authentication required"):
        super().__init__(message=message, status_code=401)

class AuthorizationError(AppException):
    """Authorization failed."""
    def __init__(self, message: str = "Access denied"):
        super().__init__(message=message, status_code=403)

class ConflictError(AppException):
    """Resource conflict."""
    def __init__(self, message: str, resource_id: str = None):
        super().__init__(
            message=message,
            status_code=409,
            details={"resource_id": resource_id}
        )

# Usage
def get_user(user_id: int):
    user = find_user(user_id)
    if not user:
        raise NotFoundError("User", user_id)
    return user

Error Response Format

# response.py
from typing import Any, Optional, List
from pydantic import BaseModel
from datetime import datetime

class ErrorResponse(BaseModel):
    """Standard error response format."""
    success: bool = False
    error: str
    message: str
    status_code: int
    timestamp: datetime = datetime.utcnow()
    details: Optional[dict] = None
    errors: Optional[List[dict]] = None
    request_id: Optional[str] = None

def error_response(
    error: str,
    message: str,
    status_code: int,
    details: dict = None,
    errors: list = None,
    request_id: str = None
) -> dict:
    """Create standardized error response."""
    return ErrorResponse(
        error=error,
        message=message,
        status_code=status_code,
        details=details,
        errors=errors,
        request_id=request_id
    ).dict()

# Usage examples
# 400 Bad Request
return error_response(
    error="ValidationError",
    message="Invalid input data",
    status_code=400,
    errors=[{"field": "email", "error": "Invalid email format"}]
)

# 404 Not Found
return error_response(
    error="NotFound",
    message="User not found",
    status_code=404,
    details={"resource": "user", "id": 123}
)

FastAPI Exception Handlers

# main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(AppException)
async def app_exception_handler(request: Request, exc: AppException):
    return JSONResponse(
        status_code=exc.status_code,
        content=error_response(
            error=type(exc).__name__,
            message=exc.message,
            status_code=exc.status_code,
            details=exc.details
        )
    )

@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
    return JSONResponse(
        status_code=400,
        content=error_response(
            error="ValueError",
            message=str(exc),
            status_code=400
        )
    )

@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
    return JSONResponse(
        status_code=500,
        content=error_response(
            error="InternalServerError",
            message="An unexpected error occurred",
            status_code=500,
            details={"path": str(request.url)}
        )
    )

Flask Error Handling

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.errorhandler(AppException)
def handle_app_exception(error):
    response = error_response(
        error=type(error).__name__,
        message=error.message,
        status_code=error.status_code,
        details=error.details
    )
    return jsonify(response), error.status_code

@app.errorhandler(404)
def handle_not_found(error):
    return jsonify(error_response(
        error="NotFound",
        message="Resource not found",
        status_code=404
    )), 404

@app.errorhandler(500)
def handle_server_error(error):
    return jsonify(error_response(
        error="InternalServerError",
        message="Server error",
        status_code=500
    )), 500

Practice Problems

  1. Create a custom exception for database operations
  2. Implement error logging alongside error responses
  3. Add error codes for different error types
  4. Create exception handlers for async operations
  5. Implement retry logic for transient errors

Advertisement

Advertisement

Need More Practice?

Get personalized Python help from ChatWhole's AI-powered platform.

Get Expert Help →