Files
voidea/app/core/exceptions.py
T

140 lines
3.4 KiB
Python

"""Custom exceptions for VoIdea application."""
from typing import Any
class AppError(Exception):
"""Base application exception.
All business errors inherit from this class.
Does not contain HTTP status codes (those are in API layer).
"""
def __init__(
self,
message: str,
code: str = "GENERAL_ERROR",
details: dict | None = None,
) -> None:
self.message = message
self.code = code
self.details = details or {}
super().__init__(message)
def to_dict(self) -> dict:
"""Convert exception to dictionary."""
return {
"error": self.code,
"message": self.message,
"details": self.details,
}
class NotFoundError(AppError):
"""Resource not found error."""
def __init__(
self,
message: str = "Resource not found",
code: str = "NOT_FOUND",
resource: str | None = None,
resource_id: str | None = None,
) -> None:
details = {}
if resource:
details["resource"] = resource
if resource_id:
details["resource_id"] = resource_id
super().__init__(message, code, details)
class PermissionError(AppError):
"""Permission denied error."""
def __init__(
self,
message: str = "Permission denied",
code: str = "PERMISSION_DENIED",
) -> None:
super().__init__(message, code)
class ValidationError(AppError):
"""Validation error."""
def __init__(
self,
message: str = "Validation error",
code: str = "VALIDATION_ERROR",
field: str | None = None,
value: Any = None,
) -> None:
details = {}
if field:
details["field"] = field
if value is not None:
details["value"] = str(value)
super().__init__(message, code, details)
class AuthenticationError(AppError):
"""Authentication error."""
def __init__(
self,
message: str = "Authentication failed",
code: str = "AUTH_FAILED",
) -> None:
super().__init__(message, code)
class QuotaExceededError(AppError):
"""Quota exceeded error."""
def __init__(
self,
message: str = "Quota exceeded",
code: str = "QUOTA_EXCEEDED",
limit: int | None = None,
used: int | None = None,
) -> None:
details = {}
if limit is not None:
details["limit"] = limit
if used is not None:
details["used"] = used
super().__init__(message, code, details)
class ExternalServiceError(AppError):
"""External service error (AI, OAuth, etc.)."""
def __init__(
self,
message: str = "External service error",
code: str = "EXTERNAL_SERVICE_ERROR",
service: str | None = None,
) -> None:
details = {}
if service:
details["service"] = service
super().__init__(message, code, details)
class RateLimitError(AppError):
"""Rate limit exceeded error."""
def __init__(
self,
message: str = "Rate limit exceeded",
code: str = "RATE_LIMIT_EXCEEDED",
retry_after: int | None = None,
) -> None:
details = {}
if retry_after:
details["retry_after"] = retry_after
super().__init__(message, code, details)
# Import for type hints
from typing import Any