## TL;DR

Google rotates refresh tokens, so reusing an old refresh token fails with invalid_grant. Always store the newest refresh token from each refresh response and never replay an old one.

## Error

```text
invalid_grant: Token has been expired or revoked (refresh token reuse)
```

## Steps

1. Check whether your code reuses a stored refresh token after a successful refresh Expected: you find the replay.
2. Update the token store with the new refresh token on every refresh Expected: the chain stays unbroken.
3. Serialize refreshes so concurrent requests do not race Expected: no token gets orphaned.
4. Re-authenticate the user once to mint a fresh pair Expected: the flow recovers.

## When to use

- invalid_grant right after a successful refresh
- Google rotated the token and the client replayed the old one

## When not to use

- The refresh token was revoked by the user (then re-auth is the only fix)
- First-ever refresh fails (then the code or client id is wrong)

## Tool compatibility

- Google OAuth refresh token rotation
- Generated OAuth clients

## Variant phrasings

### google refresh invalid_grant

You replayed a rotated refresh token. Store the newest one from each refresh.

### refresh token reuse google

Same: rotation. Never reuse a refresh token.

## Why it happens

Google rotates refresh tokens as a security measure. Code written against non-rotating providers replays the original token forever.

## Edge cases

- There is a short grace period for the old token in some cases; do not rely on it
- Multiple app instances must share one token store

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_P_2RTOyQQWMMr078cuZ-Hw
