Error codes
Two error response formats, every error code, and a troubleshooting order.
MuoCeon's OAuth interfaces use two error formats depending on where the failure happens. Your client must parse both — do not treat the unified envelope as a site-wide convention.
Format 1: protocol endpoints (RFC 6749 §5.2)
Applies to token, revoke, introspect. These endpoints must speak the standard language, or standard libraries cannot interpret them:
{
"error": "invalid_grant",
"error_description": "The authorization code has already been used"
}error | HTTP | Meaning | Where to look |
|---|---|---|---|
invalid_request | 400 | Missing or duplicated parameters, unsupported grant_type | Check form fields and Content-Type |
invalid_client | 401 | Client authentication failed | Wrong secret, wrong Basic encoding, public client wrongly sending a secret |
invalid_grant | 400 | Code/refresh token invalid, used or expired; PKCE or redirect_uri mismatch | A code exchanges once only; redirect_uri must match verbatim |
unauthorized_client | 400 | The client may not use this grant type | E.g. a third party using client_credentials |
invalid_scope | 400 | Invalid scope, or attempting to widen on refresh | Only registered delegable scopes |
server_error | 500 | Server-side failure | Report with the request time and client_id |
Format 2: unified envelope
Applies to preauthorize, authorize, grants and other internal or management endpoints, plus rate-limit responses everywhere:
{
"success": false,
"code": "OAUTH_INVALID_REDIRECT_URI",
"message": "Redirect URI is not registered for this client: https://...",
"details": ["redirect_uri must match a registered value"],
"timestamp": "2026-01-01T00:00:00.000Z"
}code | HTTP | Meaning |
|---|---|---|
OAUTH_CLIENT_NOT_FOUND | 400 | client_id is not registered |
OAUTH_INVALID_REDIRECT_URI | 400 | Redirect URI unregistered or shape-invalid |
OAUTH_INVALID_REQUEST | 400 | Authorization request parameters are malformed |
OAUTH_INVALID_CONSENT_TICKET | 400 | Consent ticket missing, expired, consumed, or bound to a different client/account/session |
OAUTH_INVALID_AUTHORIZATION_DETAILS | 400 | authorization_details type/action not on the allowlist |
OAUTH_INVALID_ASSERTION | 401 | Identity assertion invalid or expired |
OAUTH_ASSERTION_REPLAY | 400 | Identity assertion already used |
INSUFFICIENT_USER_AUTHENTICATION | 401 | Authentication strength insufficient; step-up needed (ticket is not consumed) |
OAUTH_TOO_MANY_REQUESTS | 429 | Rate limited; response carries Retry-After |
UNAUTHENTICATED | 401 | Resource endpoints: access token invalid or expired |
FORBIDDEN | 403 | Resource endpoints: audience or scope requirements not met |
Ticket errors are deliberately not split
OAUTH_INVALID_CONSENT_TICKET merges "does not exist", "already consumed" and "binding mismatch" into
one error: splitting them would tell an attacker "the ticket is genuine, only the client is wrong".
For legitimate clients all three mean the same thing — start the authorization flow again.
Format 3: redirect errors
When the user denies or the authorization request is rejected, the error appears on the redirect URI rather than in an API response:
https://your-app.example.com/callback?error=access_denied&state=k3Jd9x&iss=https%3A%2F%2Fid.example.com%2Fv1%2FoauthValidate state and iss first, then handle error. access_denied means the user explicitly refused — do not retry automatically; that only makes them refuse again.
Rate limiting
- Each endpoint counts independently; exceeding
tokendoes not affectjwksfor the same IP. - Response:
429+Retry-After(seconds). - Honour
Retry-After; do not retry immediately. Use randomised backoff for batch refreshes.
Troubleshooting order
When something fails, locate it in this order; it covers the vast majority of cases:
- Parameters: does
redirect_urimatch the registered value verbatim (trailing slash, port)? - Client: does the confidential client send its secret, does a public client wrongly send one, is
client_idcorrect? - Code / refresh token: already used (codes are single-use, refresh rotates)?
- PKCE: do
code_verifierandcode_challengepair up, and is the methodS256? - Audience and scope: is UserInfo called with an
authaudience token; were scopes filtered? - Authentication strength: do
acr_values/max_agedemand too much?