Error

OAuth invalid_grant โ€” Fix

Updated April 2026

Reading this article? Verify your fix in real-time. Debug invalid_grant now โ€” OAuthFixer โ†’
OAuth Error Response
{"error": "invalid_grant", "error_description": "Invalid authorization code"}

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