OAuth invalid_grant โ Fix
Updated April 2026
invalid_grant has four causes. Each one feels the same from the outside. Here is how to tell them apart and fix each one.
Cause 1 โ Authorization code expired (most common)
Authorization codes expire in 10 minutes or less. Exchange the code immediately after the redirect โ do not wait.
Cause 2 โ Code reused
Each code can only be used once. React re-renders, duplicate API calls, or retries can call the exchange endpoint twice. The second call gets invalid_grant.
// Prevent double exchange
const exchanging = sessionStorage.getItem('exchanging');
if (exchanging) return;
sessionStorage.setItem('exchanging', 'true');
await exchangeCode(code);
sessionStorage.removeItem('exchanging');
Cause 3 โ PKCE verifier mismatch
The code_verifier must match the code_challenge sent in the authorization request. If you regenerate it on the callback page, it will not match. Store it before redirecting, retrieve on callback.
Cause 4 โ Refresh token revoked or rotated
If you are getting invalid_grant on a refresh call, the refresh token was revoked (user revoked access, password changed) or you used a stale token after rotation. Clear tokens and re-authenticate.
try { const tokens = await refreshToken(storedRefreshToken);
} catch (err) { if (err.error === 'invalid_grant') { clearStoredTokens(); redirectToLogin(); }
} Debug your invalid_grant error โ OAuthFixer