OAuth Errors
Whatalo OAuth 2.1 error codes, their meaning, and how to resolve them.
Whatalo OAuth errors follow the standards RFC 6749 §4.1.2.1, RFC 6749 §5.2, and RFC 7009. The format and delivery channel of the error depend on which endpoint it occurs on.
Error format
The token, introspection, and revocation endpoints always return JSON:
{
"error": "invalid_grant",
"error_description": "Authorization code has expired or was already used"
}Error code table
| Code | HTTP | Endpoint(s) | Cause | Resolution |
|---|---|---|---|---|
invalid_request | 400 | All | Missing, duplicate, or malformed parameter | Check the required parameters for the endpoint |
unauthorized_client | 401 | /authorize, /token | The client is not authorized for the requested grant type | Verify the grant_type is in the registered grant_types |
access_denied | 302 / HTML | /authorize | The user declined consent | Show an appropriate message to the user |
unsupported_response_type | 400 | /authorize | response_type other than code | Use response_type=code |
invalid_scope | 400 | /authorize, /token | Unknown scope or scope exceeding those granted | Check the scope table |
server_error | 500 | All | Internal Whatalo error | Retry with exponential backoff |
temporarily_unavailable | 503 | All | Temporary maintenance or overload | Retry after the indicated Retry-After |
invalid_client | 401 | /token, /introspect, /revoke | Incorrect or missing client_secret | Verify client credentials |
invalid_grant | 400 | /token | Code expired, already used, or incorrect PKCE code_verifier | Restart the authorization flow |
invalid_redirect_uri | 400 | /authorize | redirect_uri not registered for the client | Register the exact URI via DCR |
invalid_target | 400 | /authorize, /token | Invalid or disallowed resource parameter (RFC 8707) | Verify the resource parameter value |
rate_limit_exceeded | 429 | /token, /register | Rate limit exceeded | Wait for the time indicated in Retry-After |
Error routing
The channel through which you receive the error depends on whether Whatalo can validate the client_id and redirect_uri:
Redirectable errors (authorization flow)
When the client_id and redirect_uri are valid and known, Whatalo redirects the error back to your app:
https://my-app.com/oauth/callback?
error=access_denied
&error_description=The+user+cancelled+consent
&state=ORIGINAL_STATE_VALUEErrors that are redirected: access_denied, invalid_scope, unsupported_response_type, server_error, temporarily_unavailable.
Non-redirectable errors
When Whatalo cannot verify the client_id or redirect_uri (because they are invalid or unknown), it cannot redirect — doing so could be an attack vector. In that case, Whatalo displays a branded error page at /oauth/error.
Errors that show a branded page: invalid_client, invalid_redirect_uri.
Token endpoint errors
The /oauth/token, /oauth/introspect, and /oauth/revoke endpoints always return JSON — never HTML or redirects.
Recommended handling
Exponential backoff for 429 and 503
async function callWithRetry(fn, maxAttempts = 4) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
if (err.status !== 429 && err.status !== 503) throw err;
const retryAfter = err.headers?.get("Retry-After");
const waitMs = retryAfter
? Number(retryAfter) * 1000
: Math.pow(2, attempt) * 500;
await new Promise((r) => setTimeout(r, waitMs));
}
}
throw new Error("Max retry attempts exceeded");
}Handling invalid_grant
invalid_grant on the token endpoint means the authorization flow must be restarted:
async function refreshAccessToken(refreshToken) {
const res = await fetch("https://app.whatalo.com/oauth/token", { ... });
const data = await res.json();
if (data.error === "invalid_grant") {
// Token family revoked — user must re-authorize
clearStoredTokens();
redirectToAuthorizationFlow();
return;
}
if (!res.ok) throw new Error(data.error_description);
return data;
}Rate limits per OAuth endpoint
| Endpoint | Limit |
|---|---|
POST /oauth/token | 60 req/min per IP + 30 req/min per client_id |
POST /oauth/register | 10 req/hour per IP |
POST /oauth/introspect | Included in the general API limit |
POST /oauth/revoke | Included in the general API limit |
For general REST API limits, see Rate Limits.