Common errors
TL;DR — The errors you’ll actually hit while wiring things up, in the order you’ll hit them. Each has cause + fix. Full domain-error catalog + OAuth error codes + WWW-Authenticate patterns live in Reference: Errors — this page is the operator-focused subset.
Authentication errors
401 invalid_token on every request, no other detail
Cause: wrong aud — token issued for a different resource URI than yours.
Fix: ensure the client sends resource=<your-uri> on /oauth/authorize and /oauth/token. The URI must match the resource registration byte-for-byte. See Configuration → Resources for the URI-mismatch trap.
401 invalid_dpop_proof when the client IS sending a DPoP header
Cause A (Python): forgot install_request_context(mcp) — the adapter can’t build the DPoP request context without it, so every proof fails closed.
Cause B (any language): reverse proxy rewrote the scheme or host. The server derives htu from r.Host and only honors X-Forwarded-Proto for the scheme (nothing for host) — so if the proxy is presenting a different Host to AuthPlane than the client is signing over, DPoP fails closed.
Fix A: add the install_request_context(mcp) call after mcp = FastMCP(...).
Fix B: configure the proxy to preserve the original Host header (nginx: proxy_set_header Host $host; — the default forwards $proxy_host, which is wrong). If you’re terminating TLS at the proxy, also set X-Forwarded-Proto: https. See Guides: Enable DPoP → reverse-proxy trap.
401 invalid_dpop_proof "jti already used" on the second request from the same client
Cause: client is reusing the same DPoP proof jti across requests.
Fix: client bug — generate a fresh UUID per proof. Every AuthPlane SDK does this correctly; if you’re implementing a client yourself, this is the first thing to check.
401 use_dpop_nonce on the first DPoP request
Cause: AS requires nonce (dpop.require_nonce: true); client didn’t send one.
Fix: client extracts the DPoP-Nonce header from the 401 response and includes it in the next proof’s nonce claim. All AuthPlane SDKs handle this automatically.
403 insufficient_scope on a specific tool
Cause: the token’s scope claim doesn’t contain what require_scope demands.
Fix: check granted scopes in the Admin UI Issuances panel. May need to expand scopes on the resource registration, or the client’s scope field on registration.
OAuth flow errors
400 invalid_grant "authorization code has already been used"
Cause: client tried to exchange the same code twice, or the code expired (AuthCodeTTL = 10 minutes — codes are single-use).
Fix: restart the flow — codes are single-use. If you’re seeing this constantly, check whether your client is retrying after network errors (retry logic should NOT re-use the same code).
400 invalid_grant "PKCE verification failed"
Cause: client sent wrong code_verifier or generated the wrong code_challenge.
Fix: ensure code_challenge = BASE64URL(SHA256(code_verifier)). Common bug: URL encoding the verifier before hashing, or hashing the wrong string.
400 invalid_grant "refresh token has already been used" OR "token family revoked due to reuse detection"
Cause: refresh token used twice — either concurrent refresh (client bug) or theft. AuthPlane treats every refresh-token reuse as theft and revokes the entire token family. Fix: restart auth from scratch. All tokens in the family are revoked. If you keep hitting this, your client has a concurrency bug — serialize refresh calls per token family.
400 unsupported_grant_type on client_credentials
Cause: client_credentials.enabled: false (the default).
Fix: set AUTHPLANE_CLIENT_CREDENTIALS_ENABLED=true and restart.
400 unsupported_grant_type on token-exchange or jwt-bearer
Cause: same — grants are disabled by default.
Fix: AUTHPLANE_TOKEN_EXCHANGE_ENABLED=true or xaa.enabled: true (YAML only).
400 unauthorized_client
Cause: client’s registration grant_types doesn’t include the requested grant.
Fix: update the client via authserver admin client update or re-register.
400 invalid_scope
Cause: requested scope not registered on the target resource. Fix: add the scope to the resource, or drop it from the request. Scopes must be declared at the resource level, not just on the client.
Broker / upstream errors
400 consent_required + consent_url in the token-exchange response
Cause: user hasn’t completed the Connect flow for a Broker resource (consent_url will point at /connect/{provider}), OR requested scopes exceed what the user consented to (consent_url will point at re-consent).
Fix: MCP SDK auto-translates this to MCP JSON-RPC -32042; MCP client shows the URL to the user. If you’re seeing raw consent_required, your SDK’s elicitation wiring is broken — check the SDK’s URL elicitation setup.
MCP client shows -32042 URL elicitation required
Cause: same as above — token-exchange consent needed.
Fix: follow the URL in error.data.url (Connect flow or re-consent). SDK / client should do this automatically.
Configuration & boot errors
Boot fails with "session.secret is required"
Cause: server.issuer is not localhost and session.secret is unset.
Fix: openssl rand -hex 32 and set AUTHPLANE_SESSION_SECRET.
Boot fails with "admin.api_key is required"
Cause: same — production issuer without an API key.
Fix: generate one and set AUTHPLANE_ADMIN_API_KEY.
Boot fails with "data_encryption required when broker_providers configured"
Cause: you registered broker providers but didn’t enable encryption at rest.
Fix: set data_encryption.driver to aes_master (simplest) or vault_transit_encrypt (enterprise). See Guides: Wire up the Token Vault → Step 1.
Admin UI shows “Failed to fetch” everywhere
Cause: CORS not configured; browser blocks calls to :9001 from a different origin.
Fix: set AUTHPLANE_SERVER_ALLOWED_ORIGINS with the origin of your admin UI host.
Discovery errors
PRM 404 at /.well-known/oauth-protected-resource
Cause: SDK didn’t mount the PRM handler (Go only; Python/TS auto-mount).
Fix (Go): http.Handle(adapter.WellKnownPRMPath(), adapter.ProtectedResourceMetadataHandler()).
AS metadata 404 at /.well-known/oauth-authorization-server
Cause: MCP client is hitting the wrong URL — probably your resource server instead of AuthPlane.
Fix: check that PRM’s authorization_servers field points at AuthPlane, not your MCP server’s URL.
MCP Inspector says “no tools”
Cause: Inspector URL points at the host root without /mcp.
Fix: use npx @modelcontextprotocol/inspector http://localhost:8080/mcp — with the /mcp path or whatever your SDK’s endpoint is configured for.
When you can’t find it here
- Reference: Errors — complete catalog with error envelope + all
WWW-Authenticatepatterns - Troubleshooting: Debugging — end-to-end debugging checklist
- Troubleshooting: Getting help — where to file issues and get support
Related
- Reference: Errors — full domain error catalog
- Guides: Enable DPoP end-to-end — DPoP-specific errors
- Guides: Federate to your IdP — OIDC federation errors
- Guides: Enterprise-Managed Auth — XAA errors