"""Application exception hierarchy. All custom exceptions inherit from AppError. The global exception handler in middleware.py catches these and returns the appropriate JSON envelope. """ class AppError(Exception): """Base application error.""" status_code: int = 500 message: str = "Internal server error" def __init__(self, message: str | None = None): if message is not None: self.message = message super().__init__(self.message) class ValidationError(AppError): status_code = 400 message = "Validation error" class NotFoundError(AppError): status_code = 404 message = "Resource not found" class DuplicateError(AppError): status_code = 409 message = "Resource already exists" class AuthenticationError(AppError): status_code = 401 message = "Authentication required" class AuthorizationError(AppError): status_code = 403 message = "Insufficient permissions" class ProviderError(AppError): status_code = 502 message = "Market data provider unavailable" class RateLimitError(AppError): status_code = 429 message = "Rate limited"