"""Domain exception hierarchy — framework-agnostic. Services raise these; the API layer maps them to HTTP responses (see ``app.api.errors``). The domain never references HTTP status codes. """ class DomainError(Exception): """Base for all expected, business-meaningful failures. ``code`` is a stable, machine-readable identifier returned to clients. """ code: str = "domain_error" def __init__(self, message: str | None = None) -> None: super().__init__(message or self.__class__.__doc__ or self.code) self.message = str(self) class NotFoundError(DomainError): """Requested resource does not exist.""" code = "not_found" class AlreadyExistsError(DomainError): """Resource conflicts with an existing one (e.g. duplicate).""" code = "already_exists" class ConflictError(DomainError): """Operation conflicts with current state (e.g. stale version on write).""" code = "conflict" class ValidationError(DomainError): """Input is well-formed but violates a business rule.""" code = "validation_error" class AuthenticationError(DomainError): """Caller could not be authenticated.""" code = "authentication_error" class PermissionDeniedError(DomainError): """Caller authenticated but not authorized for this action.""" code = "permission_denied" class DependencyUnavailableError(DomainError): """An external dependency (source, ML, MusicBrainz) is unavailable. Callers should degrade gracefully rather than propagate as a hard 500. """ code = "dependency_unavailable"