Skip to main content

Error Codes Reference

LumoAuth returns consistent error responses across all API endpoints. This reference helps you handle errors and troubleshoot issues.

Error Response Format

All error responses follow this structure:

{
"error": "error_code",
"error_description": "Human-readable description of the error",
"error_uri": "https://docs.lumoauth.com/errors/error_code"
}

OAuth 2.0 Errors

Standard OAuth 2.0 error codes returned by the authorization and token endpoints.

Error CodeHTTP StatusDescriptionCommon Causes
invalid_request400The request is malformed or missing required parametersMissing grant_type, invalid JSON body, unsupported content type
invalid_client401Client authentication failedWrong client_id, wrong client_secret, client not found in tenant
invalid_grant400The authorization code or refresh token is invalidCode expired, code already used, refresh token revoked
unauthorized_client401Client is not authorized to use this grant typeTrying to use client_credentials when not enabled
unsupported_grant_type400The grant type is not supportedTypo in grant_type, using deprecated grant
invalid_scope400The requested scope is invalid or not allowedScope not registered, exceeds client's allowed scopes
access_denied403The user or authorization server denied the requestUser clicked "Deny", consent required but not granted
server_error500Unexpected server errorInternal server error, database unavailable
temporarily_unavailable503Server is temporarily unavailableMaintenance mode, rate limiting, overload

Authorization API Errors

Error codes specific to the permission check endpoints.

Error CodeHTTP StatusDescription
MISSING_PERMISSION400Required field 'permission' is missing from request
INVALID_PERMISSIONS400Permissions field must be a non-empty array
MISSING_FIELDS400Required fields are missing (Zanzibar check: object, relation, user)
CHECK_FAILED500Permission check failed due to server error
ZANZIBAR_CHECK_FAILED400Zanzibar relationship check failed (invalid format or unknown namespace)
LIST_FAILED500Failed to retrieve user permissions

Token Introspection Errors

Error CodeHTTP StatusDescription
invalid_token401The token provided is expired, revoked, or malformed
insufficient_scope403The token doesn't have the required scope for this operation

Agent & Token Exchange Errors

Error CodeHTTP StatusDescription
invalid_target400Token exchange target (audience) is invalid
unsupported_token_type400The subject_token_type or actor_token_type is not supported
delegation_not_allowed403Client is not configured to allow token exchange/delegation
budget_exceeded429Agent has exceeded its configured budget limits
capability_denied403Agent lacks the required capability for this operation
workload_identity_failed401Failed to verify workload identity token

Dynamic Client Registration Errors

Error CodeHTTP StatusDescription
invalid_redirect_uri400One or more redirect URIs are invalid or use a non-allowed scheme
registration_not_allowed403Dynamic client registration is disabled for this tenant
invalid_client_metadata400Client metadata is invalid or contains unsupported values

HTTP Status Code Summary

StatusMeaningAction
200SuccessRequest completed successfully
400Bad RequestFix the request parameters or body
401UnauthorizedCheck credentials, refresh token, or re-authenticate
403ForbiddenUser/client lacks permission for this action
404Not FoundCheck the endpoint URL and tenant slug
429Too Many RequestsBack off and retry after the specified time
500Server ErrorRetry with exponential backoff; contact support if persistent
503Service UnavailableRetry later; check status page

Error Handling Best Practices

import requests
import time

class LumoAuthClient:
MAX_RETRIES = 3

def call_api(self, method, url, **kwargs):
for attempt in range(self.MAX_RETRIES):
response = requests.request(method, url, **kwargs)

if response.status_code == 200:
return response.json()

# Handle specific errors
if response.status_code == 401:
# Token expired - refresh and retry
self.refresh_token()
continue

if response.status_code == 429:
# Rate limited - back off
retry_after = int(response.headers.get('Retry-After', 60))
time.sleep(retry_after)
continue

if response.status_code >= 500:
# Server error - exponential backoff
time.sleep(2 ** attempt)
continue

# Client error - don't retry
error = response.json()
raise LumoAuthError(
code=error.get('error'),
message=error.get('error_description')
)

raise LumoAuthError(code='max_retries', message='Max retries exceeded')