Skip to content

Exceptions

All library-specific failures are expressed as LitestarAuthError (or a subclass). Each exception carries a human-readable message and a stable code string. The library's machine-readable codes live on the ErrorCode StrEnum: member names match their string values, so codes serialize cleanly to JSON and compare naturally to plain strings. The enum includes UNKNOWN, used as the default for base LitestarAuthError instances and as the JSON code fallback for plugin-owned auth routes when a ClientException has no string code in extra.

Concrete types group related failures: AuthenticationError covers login and identity problems (with narrower types such as UserAlreadyExistsError, UserNotExistsError, InvalidPasswordError, InactiveUserError, UnverifiedUserError, and OAuthAccountAlreadyLinkedError). UserAlreadyExistsError accepts keyword-only duplicate context via identifier=UserIdentifier(...), mirrors that payload onto identifier_type and identifier_value, and keeps the default message generic so public responses do not reveal the colliding identifier. InvalidPasswordError keeps optional operator-only user_id context and uses the same keyword-only constructor shape. OAuthAccountAlreadyLinkedError carries the conflicting provider identity on the exception instance via provider, account_id, and existing_user_id, and its default message includes that context for operator-facing logs. TokenError covers token lifecycle issues (for example InvalidVerifyTokenError and InvalidResetPasswordTokenError). AuthorizationError is raised when the caller is authenticated but not permitted; structured role-guard failures use the dedicated INSUFFICIENT_ROLES code, and structured permission-guard failures use INSUFFICIENT_PERMISSIONS while keeping required/granted permission names off default messages. ConfigurationError indicates invalid plugin or library configuration. Not every ErrorCode has a dedicated exception class—some codes are used directly where a shared shape is enough.

The bundled HTTP controllers translate these exceptions into HTTP responses (status codes and structured error payloads) without manual mapping in typical apps. For a table of codes, HTTP mapping, and user-facing messages, see Errors reference. The same ErrorCode may appear with different HTTP statuses depending on context — for example, TOKEN_PROCESSING_FAILED is usually 401 or 400 for invalid tokens, and 503 when denylist storage cannot accept a required revocation or pending-JTI write under capacity pressure (see the errors reference and Security).

litestar_auth.exceptions

Custom exception hierarchy for litestar-auth.

ApiKeyError(message=None, code=None)

Bases: LitestarAuthError

Raised when an API-key manager operation fails.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

ApiKeyLimitReachedError(*, max_keys_per_user, message=None, code=None)

Bases: ApiKeyError

Raised when a user has reached the configured active API-key limit.

Initialize the limit error with structured policy context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    max_keys_per_user: int,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the limit error with structured policy context."""
    self.max_keys_per_user = max_keys_per_user
    super().__init__(message=message, code=code)

ApiKeyNotFoundError(message=None, code=None)

Bases: ApiKeyError

Raised when an API key cannot be found in the caller's ownership scope.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

ApiKeyScopeDeniedError(*, denied_scopes, message=None, code=None)

Bases: ApiKeyError

Raised when requested API-key scopes are outside the configured whitelist.

Initialize the scope-denial error with structured scope context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    denied_scopes: frozenset[str],
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the scope-denial error with structured scope context."""
    self.denied_scopes = denied_scopes
    super().__init__(message=message, code=code)

AuthenticationError(message=None, code=None)

Bases: LitestarAuthError

Raised when authentication fails.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

AuthorizationError(message=None, code=None)

Bases: LitestarAuthError

Raised when an authenticated user is not allowed to perform an action.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

ConfigurationError(message=None, code=None)

Bases: LitestarAuthError

Raised when the library is configured incorrectly.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

ExpiredOrganizationInvitationTokenError(message=None, code=None)

Bases: InvalidOrganizationInvitationTokenError

Raised when an organization invitation token or row is expired.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

InactiveUserError(message=None, code=None)

Bases: AuthenticationError

Raised when an operation requires an active account.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

InsufficientOrganizationPermissionsError(*, required_permissions, granted_permissions, require_all, message=None, code=None)

Bases: InsufficientPermissionsError

Raised when a user does not satisfy an organization permission check.

Initialize the organization permission-denial error with structured permission context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    required_permissions: frozenset[str],
    granted_permissions: frozenset[str],
    require_all: bool,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the organization permission-denial error with structured permission context."""
    required_permission_phrase = (
        "all of the required organization permissions"
        if require_all
        else "any of the required organization permissions"
    )
    resolved_message = message or f"The authenticated user does not have {required_permission_phrase}."
    super().__init__(
        required_permissions=required_permissions,
        granted_permissions=granted_permissions,
        require_all=require_all,
        message=resolved_message,
        code=code,
    )

InsufficientOrganizationRolesError(*, required_roles, user_roles, require_all, message=None, code=None)

Bases: InsufficientRolesError

Raised when a user does not satisfy an organization role check.

Initialize the organization role-denial error with structured role context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    required_roles: frozenset[str],
    user_roles: frozenset[str],
    require_all: bool,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the organization role-denial error with structured role context."""
    required_role_phrase = (
        "all of the required organization roles" if require_all else "any of the required organization roles"
    )
    resolved_message = message or f"The authenticated user does not have {required_role_phrase}."
    super().__init__(
        required_roles=required_roles,
        user_roles=user_roles,
        require_all=require_all,
        message=resolved_message,
        code=code,
    )

InsufficientPermissionsError(*, required_permissions, granted_permissions, require_all, message=None, code=None)

Bases: AuthorizationError

Raised when a user does not satisfy a permission-based authorization check.

Initialize the permission-denial error with structured permission context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    required_permissions: frozenset[str],
    granted_permissions: frozenset[str],
    require_all: bool,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the permission-denial error with structured permission context."""
    self.required_permissions = required_permissions
    self.granted_permissions = granted_permissions
    self.require_all = require_all
    required_permission_phrase = (
        "all of the required permissions" if require_all else "any of the required permissions"
    )
    # Security: permission names can reveal the application's authorization
    # model, so keep them on the exception instance for trusted hooks without
    # echoing them in the default human-readable message.
    resolved_message = message or f"The authenticated user does not have {required_permission_phrase}."
    super().__init__(message=resolved_message, code=code)

InsufficientRolesError(*, required_roles, user_roles, require_all, message=None, code=None)

Bases: AuthorizationError

Raised when a user does not satisfy a role-based authorization check.

Initialize the role-denial error with structured role context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    required_roles: frozenset[str],
    user_roles: frozenset[str],
    require_all: bool,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the role-denial error with structured role context."""
    self.required_roles = required_roles
    self.user_roles = user_roles
    self.require_all = require_all
    required_role_phrase = "all of the required roles" if require_all else "any of the required roles"
    # Security: keep structured role context on the exception instance for
    # operators and custom hooks, but do not leak role names in the
    # default human-readable message.
    resolved_message = message or f"The authenticated user does not have {required_role_phrase}."
    super().__init__(message=resolved_message, code=code)

InvalidOrganizationInvitationTokenError(message=None, code=None)

Bases: OrganizationAdminError

Raised when an organization invitation token or row state is invalid.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

InvalidPasswordError(*, user_id=None, message=None, code=None)

Bases: AuthenticationError

Raised when a password does not match the stored credentials.

Optional user_id context is stored on the instance for operator logging without changing the response message sent to clients.

Initialize the invalid-password error with optional operator-only context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    user_id: object | None = None,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the invalid-password error with optional operator-only context."""
    self.user_id = user_id
    super().__init__(message=message, code=code)

InvalidResetPasswordTokenError(message=None, code=None)

Bases: TokenError

Raised when a password reset token is invalid or expired.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

InvalidVerifyTokenError(message=None, code=None)

Bases: TokenError

Raised when an email verification token is invalid or expired.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

LitestarAuthError(message=None, code=None)

Bases: Exception

Base exception for all library-specific errors.

Initialize the exception with a default or custom message and explicit or inherited code.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OAuthAccountAlreadyLinkedError(*, provider, account_id, existing_user_id, message=None, code=None)

Bases: AuthenticationError

Raised when an OAuth provider identity is already linked to another user.

Persistence layer refuses cross-user rebinding: one provider identity (oauth_name, account_id) can only be linked to a single local user.

Initialize the linked-account conflict with provider context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    provider: str,
    account_id: str,
    existing_user_id: object,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the linked-account conflict with provider context."""
    self.provider = provider
    self.account_id = account_id
    self.existing_user_id = existing_user_id
    resolved_message = (
        message
        or f"OAuth account {self.provider}:{self.account_id} is already linked to user {self.existing_user_id}"
    )
    super().__init__(message=resolved_message, code=code)

OrganizationAdminError(message=None, code=None)

Bases: LitestarAuthError

Raised when an organization-admin operation fails.

Concrete subclasses carry a specific default_code; the base keeps the generic ErrorCode.UNKNOWN inherited from :class:LitestarAuthError.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OrganizationAlreadyExistsError(message=None, code=None)

Bases: OrganizationAdminError

Raised when an organization slug is already assigned.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OrganizationInvitationEmailMismatchError(message=None, code=None)

Bases: InvalidOrganizationInvitationTokenError

Raised when the authenticated user does not own the invited email address.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OrganizationLastPrivilegedMemberError(message=None, code=None)

Bases: OrganizationAdminError

Raised when an operation would remove the final privileged organization member.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OrganizationMembershipAlreadyExistsError(message=None, code=None)

Bases: OrganizationAdminError

Raised when an organization membership already exists.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OrganizationMembershipNotFoundError(message=None, code=None)

Bases: OrganizationAdminError

Raised when an organization membership lookup target is unknown.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

OrganizationNotFoundError(message=None, code=None)

Bases: OrganizationAdminError

Raised when an organization-admin lookup target is unknown.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

RefreshSessionNotFoundError(message=None, code=None)

Bases: TokenError

Raised when a user-scoped refresh session cannot be found.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

SecurityWarning

Bases: UserWarning

Warning emitted for security-sensitive insecure defaults.

SessionManagementUnsupportedError(message=None, code=None)

Bases: TokenError

Raised when a strategy cannot manage refresh sessions.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

TokenError(message=None, code=None)

Bases: LitestarAuthError

Raised when token operations fail.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

UnverifiedUserError(message=None, code=None)

Bases: AuthenticationError

Raised when an operation requires a verified account.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

UserAlreadyExistsError(*, identifier=None, message=None, code=None)

Bases: AuthenticationError

Raised when creating a user that already exists.

Duplicate identifier context is stored on the exception instance for operator logging, but the generated default message stays generic. The public register controller further maps this exception to the shared REGISTER_FAILED response so callers cannot distinguish duplicate identifiers from other registration failures.

Initialize the duplicate-user error with optional identifier context.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    *,
    identifier: UserIdentifier | None = None,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the duplicate-user error with optional identifier context."""
    self.identifier = identifier
    self.identifier_type = None if identifier is None else identifier.identifier_type
    self.identifier_value = None if identifier is None else identifier.identifier_value
    super().__init__(message=message, code=code)

UserNotExistsError(message=None, code=None)

Bases: AuthenticationError

Raised when a requested user cannot be found.

Source code in litestar_auth/exceptions.py
def __init__(
    self,
    message: str | None = None,
    code: str | None = None,
) -> None:
    """Initialize the exception with a default or custom message and explicit or inherited code."""
    resolved_message = message or self.default_message
    resolved_code = code if code is not None else self.default_code
    self.code = resolved_code
    super().__init__(resolved_message)

totp_stepup_required_exception()

Return the stable 403 client response for missing recent TOTP verification.

Source code in litestar_auth/exceptions.py
def totp_stepup_required_exception() -> ClientException:
    """Return the stable 403 client response for missing recent TOTP verification."""
    msg = "Recent TOTP verification is required."
    return ClientException(status_code=403, detail=msg, extra={"code": ErrorCode.TOTP_STEPUP_REQUIRED})