Skip to content

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_grant is terminal for that code or refresh token. Do not retry. Authorization codes are single-use and expire after 10 minutes.
  • invalid_client means the client credentials are wrong; check the Basic authentication header and the secret.
  • invalid_scope means 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 lacks openid.
  • 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 sub change for the same persona as an incident: PPIDs are stable by design.