Handle errors
Errors come from three surfaces: the OIDC endpoints, the RP machine plane, and the portal. Each uses the standard shape for its surface. The exhaustive, closed code sets are in the error-code reference.
Authorization endpoint
Errors are returned to your redirect URI as query parameters, together with
the state you sent:
https://app.example.com/callback?error=access_denied&error_description=...&state=<state>
Always validate state before acting on an error. A missing or mismatched
state is an attack signal, not a user outcome.
The common outcomes:
access_denied— the user denied consent or cancelled. Show a normal cancellation; do not retry automatically.login_required/consent_required— restart the flow.invalid_request,unauthorized_client,unsupported_response_type,invalid_scope— fix the client configuration or request.server_error,temporarily_unavailable— retry later with backoff.
Full set: authorization endpoint errors.
Token endpoint
The token endpoint returns JSON:
{
"error": "invalid_grant",
"error_description": "..."
}
Key distinctions:
invalid_grantis terminal for that code or refresh token. Do not retry. Authorization codes are single-use and expire after 10 minutes.invalid_clientmeans the client credentials are wrong; check the Basic authentication header and the secret.invalid_scopemeans the requested scope is not allowed.
Full set: token endpoint errors.
UserInfo and the machine plane
Both return a JSON error object:
{ "error": "invalid_token" }
401 invalid_token— the token is missing, expired or revoked. Refresh or re-authenticate; do not retry the same token.403 insufficient_scope— the token lacksopenid.502 temporarily_unavailable— retry with backoff.
The machine plane is bearer-only and ignores cookies.
Full set: UserInfo and machine-plane errors.
Portal and registration errors
Portal errors are a fixed code in a closed set:
{ "status": "error", "error": "unverified_host" }
Use the code, not the human text. Common causes: an unproven host
(unverified_host), a local-development RP using a non-loopback host
(local_development_non_loopback), a redirect-host change
(identity_break), a stale session (step_up_required) or a version conflict
(conflict).
Full set: portal and registration errors.
General rules
- Do not log tokens, authorization codes, claim values or full error bodies containing them.
- Do not retry non-retryable errors (
access_denied,invalid_grant,invalid_client). Retry only transient failures with backoff. - Show users a neutral message; keep protocol details in your server logs, by error code and request id where available.
- Treat an unexpected
subchange for the same persona as an incident: PPIDs are stable by design.