Errors
Every error this server returns, and what usually causes it.
Error codes
| Code | HTTP | Usual cause |
|---|---|---|
OAUTH_INVALID_CLIENT | 401 | Unknown client_id, wrong client_secret, or the client has been revoked. |
OAUTH_INVALID_GRANT | 400 | Code expired, already used, issued to a different client, redirect_uri mismatch, failed PKCE, or an unsupported grant_type. |
OAUTH_INVALID_REDIRECT | 400 | The redirect_uri is not registered for this client. |
OAUTH_INVALID_SCOPE | 400 | A requested scope is not granted to this client. |
TOKEN_INVALID | 401 | Access token missing, malformed, expired or revoked. |
When the user declines, there is no error code — they are redirected to your callback with
error=access_denied and your state. Treat it as a normal outcome,
not a failure.
Troubleshooting
"Cannot authorize this app" before any consent screen
The client or redirect URI failed validation. This deliberately does not
redirect back to your application — an unverified redirect target could be used as an open
redirect. Check the client_id is right and the exact
redirect_uri is registered.
OAUTH_INVALID_GRANT on a code that looks fine
Work through, in order:
- Is the exchange happening within 60 seconds?
- Has the code already been used — including by an automatic retry?
- Is
redirect_uribyte-identical to step 1? Percent-encoding and trailing slashes count. - Is the
code_verifierthe one whose challenge you sent? Behind a load balancer, a session not shared across instances loses it.
"Invalid site key type" or JWKS errors
Your library is trying to fetch signing keys from jwks_uri, which returns an
empty set by design. Configure it to verify HS256 with your client secret —
see Tokens & refresh.
401 from /oauth/userinfo
The access token is expired or revoked. Refresh it; if the refresh also fails, the user has withdrawn access and must go through authorization again. Do not retry in a loop.
Users are re-prompted for consent
You are asking for a scope they have not previously approved. Check whether the scope set changed on your side, or was widened at registration.
Before going live
- Production redirect URI registered, and HTTPS.
stategenerated per request and verified on the callback.- PKCE verifier stored server-side, never in the URL or browser storage.
client_secretonly on your server; browser and mobile apps registered as public clients.- Users keyed on
sub. - Expired access tokens refreshed; a
401restarts authorization. - ID token signature,
iss,aud,expandnonceall verified.
Getting help
Email support@gocosys.com with your
client_id — never your secret — the full error response, and
roughly when it happened, so it can be matched against the server logs.