OAuth 2.0 and OpenID Connect error codes
OAuth 2.0 and OpenID Connect define a compact set of machine-readable error codes returned when something goes wrong at the authorization, token, resource-server, or device-flow endpoints. Each error value (paired with an optional error_description and error_uri) tells the client precisely what failed — a malformed request, a bad client credential, an expired grant, or a user interaction requirement. Knowing the exact meaning of each code, and which endpoint it comes from, collapses hours of integration debugging into minutes.
Error response shapes by endpoint
Authorization endpoint
Errors at the authorization endpoint are delivered as redirect responses — the server redirects back to the client’s redirect_uri with query parameters (for response_type=code) or fragment parameters (for implicit flows):
https://client.example.com/callback?
error=access_denied
&error_description=The+user+denied+access
&state=abc123
Codes here include access_denied, unsupported_response_type, invalid_scope, and temporarily_unavailable.
Token endpoint
Errors at the token endpoint are JSON body responses, typically with HTTP 400 Bad Request:
{
"error": "invalid_grant",
"error_description": "The authorization code has expired."
}
The exception is invalid_client, which returns HTTP 401 Unauthorized (with a WWW-Authenticate: Basic header if the client used HTTP Basic auth).
Resource server
Resource servers return errors via the WWW-Authenticate header in HTTP 401 or 403 responses, per RFC 6750:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
error="invalid_token",
error_description="The access token has expired"
Device flow (RFC 8628)
The device authorization server uses token-endpoint polling responses with codes like authorization_pending and slow_down to manage the polling loop.
The most commonly confused code pairs
invalid_client vs invalid_grant
| Error | Meaning | HTTP status | Fix |
|---|---|---|---|
invalid_client | Client authentication itself failed — bad client_id, wrong secret, or unsupported auth method | 401 | Configuration problem; check your client credentials |
invalid_grant | The grant (auth code or refresh token) is expired, revoked, used, or mismatched | 400 | Restart the auth flow; do not retry the same grant |
These two are the most commonly misdiagnosed. invalid_client is a server-side or configuration issue; invalid_grant is a flow-state issue.
login_required vs interaction_required vs consent_required
These three OpenID Connect errors all occur when a client requests prompt=none (silent authentication) but the server cannot satisfy the request silently:
login_required— the user is not authenticated at all. Redirect to an interactive login.consent_required— the user is authenticated but has not consented to the requested scopes. Redirect withprompt=consent.interaction_required— some other interaction is needed (for example, MFA, terms acceptance). Redirect withoutprompt=none.
authorization_pending vs slow_down
Both appear in the device flow polling loop at the token endpoint:
authorization_pending— the user has not yet approved on their other device. Keep polling at the current interval.slow_down— you are polling too fast. Add 5 seconds to your current polling interval and keep polling. Failure to slow down may result inaccess_denied.
Practical debugging checklist
- Which endpoint returned the error? Authorization redirect vs token JSON vs resource WWW-Authenticate — the endpoint tells you where to look.
- Is it
invalid_client(configuration) orinvalid_grant(flow state)? - For OIDC silent flows: is the user not logged in (
login_required) or logged in but requiring interaction (interaction_required,consent_required)? - In device flow: are you slowing down on
slow_down, and stopping entirely onexpired_token? - For
insufficient_scope: theWWW-Authenticateheader should name the required scope — request it in a new authorization flow.