Skip to content

Error Handling #1

Description

@kaseywright

Establish consistent error handling patterns across the API with proper HTTP status codes, structured error responses, and centralized exception management.

Tasks

1. Error Response Standards

  • Define standard error response format with fields: error_code, message, details, timestamp, request_id
  • Create error code constants for common scenarios (validation, authentication, database, etc.)
  • Define HTTP status code mapping for different error types
  • Document error response schema in OpenAPI

2. Custom Exception Classes

  • Create base FluentAIException class
  • Create specific exception types:
    • ValidationException (400)
    • AuthenticationException (401)
    • AuthorizationException (403)
    • NotFoundException (404)
    • ConflictException (409)
    • DatabaseException (500)
    • ExternalServiceException (502)

3. Request ID Tracking & Storage

  • Create RequestIDMiddleware that generates UUID for each request
  • Store request ID in request.state.request_id for downstream access
  • Add X-Request-ID header to all responses
  • Check for existing request ID headers (X-Request-ID, X-Correlation-ID) before generating

4. Error Logging & Tracking

  • Add structured logging for all exceptions with request ID context
  • Log error details: exception type, message, stack trace (debug only), request path
  • Store error context in request state for exception handlers
  • Create error logging utility functions

5. Global Exception Handler

  • Create @app.exception_handler() for each custom exception type
  • Extract request ID from request.state for error responses
  • Handle unexpected exceptions with generic 500 response
  • Include request ID in all error responses

6. Update Existing Endpoints

  • Replace HTTPException with custom exceptions in routers
  • Update error messages to be user-friendly but informative
  • Add proper validation error handling
  • Ensure database operation errors are caught and wrapped

7. Input Validation

  • Add Pydantic validation models for all request bodies
  • Implement field-level validation with helpful error messages
  • Add query parameter validation
  • Handle malformed JSON gracefully

8. Database Error Handling

  • Wrap database operations in try/catch blocks
  • Convert SQLAlchemy errors to appropriate custom exceptions
  • Handle connection errors, constraint violations, timeouts
  • Add retry logic for transient database errors

9. Configuration & Utilities

  • Add error handling settings to config (debug mode, stack traces, etc.)
  • Create utility functions for common error scenarios
  • Add error context helpers (request ID, user context, etc.)

Acceptance Criteria

  • All endpoints return consistent error response format
  • Proper HTTP status codes for all error scenarios
  • Request IDs included in all error responses for debugging
  • No raw stack traces exposed in production
  • Database errors are properly caught and transformed
  • Validation errors provide clear, actionable messages

Error Response Format

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": {
      "field": "email",
      "issue": "Invalid email format"
    },
    "timestamp": "2024-01-01T12:00:00Z",
    "request_id": "req_123456789"
  }
}

Security Requirements

  • No sensitive data leaked in error messages
  • Stack traces only in debug mode
  • Rate limit error endpoints to prevent abuse
  • Sanitize error messages for external consumption

Dependencies

  • Database connection implementation
  • Logging system implementation
  • Request ID tracking middleware

Implementation Details

Request ID Middleware Example

import uuid
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware

class RequestIDMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Check for existing request ID headers
        request_id = (
            request.headers.get("X-Request-ID") or
            request.headers.get("X-Correlation-ID") or
            str(uuid.uuid4())
        )
        request.state.request_id = request_id
        
        response = await call_next(request)
        response.headers["X-Request-ID"] = request_id
        return response

Exception Handler Example

@app.exception_handler(ValidationException)
async def validation_handler(request: Request, exc: ValidationException):
    request_id = getattr(request.state, "request_id", "unknown")
    
    # Log with request ID context
    logger.error(
        f"Validation failed: {exc.message}",
        extra={"request_id": request_id, "details": exc.details}
    )
    
    return JSONResponse(
        status_code=400,
        content={
            "error": {
                "code": "VALIDATION_ERROR",
                "message": exc.message,
                "details": exc.details,
                "timestamp": datetime.utcnow().isoformat(),
                "request_id": request_id
            }
        }
    )

Error Storage & Tracking

  • In-memory: Request ID stored in request.state.request_id
  • Logs: Structured JSON logs with request_id field
  • Headers: X-Request-ID added to all responses
  • Database: Optional error tracking table (separate ticket)

Notes

  • Follow FastAPI's exception handler patterns

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions