Skip to content

Troubleshooting

Symptom → cause → fix. Grouped by area.

Authentication

Symptom Cause Fix
401 Not authenticated on protected route No access token in header/cookie, or expired Refresh via POST /auth/refresh (body {"refresh_token":"..."} or refresh cookie).
401 Invalid email or password Wrong password, unknown email, or account locked Check token_adapter is set; if account is locked, wait security.lockout_duration seconds.
401 Account is deactivated User's is_active=False Reactivate directly in the DB or via your admin tool.
403 Sign in not allowed allow_signin hook returned False Check the hook; remember it is only called for OAuth, magic-link, passkey — not /auth/login.

Cookies / CSRF

Symptom Cause Fix
403 CSRF token missing or invalid on POST/PUT/PATCH/DELETE Cookie-mode request missing X-CSRF-Token header matching the csrf_token cookie Send the header equal to the readable csrf_token cookie, or set csrf_enabled=False if using Bearer only.
Cookies not set after login token_delivery is "json" (default) Set token_delivery="cookie".
Browser discards cookies over HTTP Secure=True over plain HTTP Use debug=True in dev, or serve over HTTPS.

Email

Symptom Cause Fix
No email arrives email_transport is None (silent no-op) Configure a transport (ConsoleTransport for dev, SMTPTransport for prod).
MissingDependencyError: 'aiosmtplib' is required... email extra not installed pip install "sreekarnv-fastauth[email]"
Verification email never sent after register Registration does not send one. Call POST /auth/request-verify-email explicitly.
Link points at localhost instead of prod URL base_url left at default Set base_url="https://app.example.com".

OAuth

Symptom Cause Fix
400 oauth_state_store is not configured Missing oauth_state_store Add it; use RedisSessionBackend in production.
400 oauth_adapter is not configured Missing oauth_adapter Add oauth_adapter=adapter.oauth.
400 Invalid OAuth state (intermittent) Multi-worker + MemorySessionBackend Use RedisSessionBackend.
ProviderError: OAuth redirect_uri is not registered redirect_uri not in oauth_allowed_redirect_uris Add the exact URL, or set oauth_allowed_redirect_uris=None (not recommended in prod).
ProviderError: OAuth provider email is not verified Existing user, provider returned email_verified=False Mark the email verified manually, or sign in via a verified provider.
400 This provider account is already linked to a user Trying to link an OAuth account that belong to another user Unlink on that user first via DELETE /auth/oauth/accounts/{provider}.

RBAC

Symptom Cause Fix
500 RBAC is not configured on require_role/require_permission or /auth/roles auth.role_adapter is None Set auth.role_adapter = adapter.role.
Roles from config.roles aren't listed on /auth/roles initialize_roles() not called in lifespan Add await auth.initialize_roles() inside the lifespan startup.
New user gets no default role default_role not set, or the user was created before the config had default_role Set default_role, or assign manually via /auth/roles/assign.
403 Insufficient role for admin endpoints User is not admin Bootstrap the first admin via direct DB write.

Database

Symptom Cause Fix
500 ... no such table: fastauth_users (or similar) create_tables() not called in lifespan Add await adapter.create_tables() to the lifespan startup.
OperationalError with multi-worker SQLite SQLite does not support multiple writers Use PostgreSQL/MySQL for real apps; SQLite is dev-only.

JWT / JWKS

Symptom Cause Fix
ConfigError: ... requires jwks_enabled=True RS256/RS512 without jwks_enabled Set jwt=JWTConfig(algorithm="RS256", jwks_enabled=True).
jwks_manager is None at first sign-in await auth.initialize_jwks() missing Call it inside the lifespan startup (after auth.mount(app)).
/.well-known/jwks.json returns {"keys": []} JWKS is disabled or initialized late Ensure jwks_enabled=True and initialize_jwks() runs on startup.
Tokens invalid after restart Auto-generated RSA keys are ephemeral Provide your own PEM private_key/public_key.
InvalidTokenError on resource service after key rotation Resource service cached an old KeySet Re-fetch /.well-known/jwks.json periodically.

Do not call initialize_jwks() before auth.mount(app). mount reads static config.jwt.jwks_enabled to register the JWKS route; initialize_jwks populates the JWKS manager at startup (inside the lifespan, which runs after mount). The lifespan order shown in examples/full/main.py is the canonical, correct pattern.