Fix OAuth invalid_grant Errors
invalid_grant means the authorization grant or refresh token cannot be accepted. Diagnose it by grant type and protect every credential while collecting evidence.
Identify the failing grant
Record the provider, token endpoint, grant type, response timestamp, correlation ID, and non-secret metadata. Never log authorization codes, refresh tokens, client secrets, access tokens, PKCE verifiers, or full token responses. An authorization-code exchange, refresh-token request, and device flow can all use the same error name for different conditions, so start with the exact flow.
Authorization code checks
Redeem an authorization code once, promptly, and at the correct token endpoint. Codes are short-lived and bound to the client and authorization request. Prevent a callback from being processed twice by browser refreshes, duplicate frontend requests, retries, or multiple application instances. If a code was already submitted, begin a new authorization flow instead of retrying it.
Match the redirect URI exactly
When the authorization request included redirect_uri, send the same value during the token exchange according to the provider rules. Compare scheme, host, port, path, trailing slash, capitalization, and encoding. Do not reconstruct the URI from an internal proxy host. Configure one public callback explicitly and preserve it through both steps.
Verify PKCE without exposing the verifier
For PKCE, retain the original high-entropy verifier in the same login transaction. Generate the challenge with the negotiated method, normally S256, and send the unchanged verifier at exchange time. A lost session, mixed browser tabs, incorrect Base64 URL encoding, padding mistakes, or hashing the verifier twice will break the binding. Log only whether the expected transaction and method were found.
Handle refresh-token rotation
A refresh token may expire, be revoked after logout or password change, or become invalid after a period of inactivity. Providers that rotate refresh tokens issue a replacement on every successful refresh. Store the new value atomically. Coordinate concurrent workers so only one refreshes an account at a time; otherwise the winner consumes the token and the loser receives invalid_grant. If recovery is not documented, require authorization again.
Check clocks, client, and environment
Synchronize server clocks and compare timestamps in UTC. Confirm the code or token belongs to the same client, tenant, environment, and provider domain used at redemption. Production credentials cannot redeem a grant issued by a sandbox authorization server. Keep client-authentication errors separate: those commonly use invalid_client, even though provider behavior varies.
Test the repaired flow
Run a new login from authorization through token exchange exactly once. Then test refresh rotation, concurrent refresh protection, revocation, and expired sessions. Inspect non-secret JWT claims locally with the JWT Decoder and callback encoding with the URL Encoder/Decoder.
Frequently Asked Questions
Can an authorization code be used twice?
No. It is short-lived and single use.
Must redirect_uri match?
Yes, when supplied, it must match according to provider rules.
Can concurrent refreshes cause this error?
Yes. Rotation can invalidate the copy used by a second worker.