TL;DR: The refresh grant is dead - refresh tokens expire, get rotated to single-use, or die when the user changes their password. Re-run the full authorization flow to get a fresh token pair, store the new refresh token (the old one will not work again), and retry. Do not loop the refresh call; it will never succeed with a dead grant.

```text
401
```

1. Stop retrying the refresh - confirm the failing call is the token endpoint using the refresh grant.
   Expected: repeated 401s on the same grant; the grant is invalid, not the network.
2. Check the usual killers: did the password change, was the integration re-authorized elsewhere, or did the refresh token pass its lifetime?
   Expected: one of these is true - that is your cause.
3. Run the full OAuth or login flow again to obtain a new access and refresh token pair.
   Expected: 200 with fresh tokens.
4. Persist the NEW refresh token immediately, replacing the old one everywhere (env, secret store, database).
   Expected: subsequent refreshes use the new grant and succeed.
5. Add handling for 401 on refresh: treat it as "re-authenticate", not "retry".
   Expected: the next expiry triggers a clean re-auth instead of a retry storm.

## Use this when
- token refresh returns 401 while other API calls used to work
- the failure started after a password change or re-authorization
- the integration sat idle past the refresh token's lifetime

## Not for this skill when
- the 401 is on regular API calls with a fresh access token (check scopes or token expiry instead)
- the error is 429 (rate limiting - back off, do not re-auth)
- you never had a working token (fix the initial auth flow first)

## Variant phrasings
### contractbook api 401 refresh token expired
### contractbook oauth token refresh unauthorized
### contractbook api re-authenticate 401

## Why it happens
Refresh tokens are long-lived but not immortal: they expire, many providers rotate them so each is single-use, and a password change or explicit revocation kills them. The token endpoint answers a dead grant with 401, which looks like a credentials problem but is really a lifecycle problem.

## Edge cases
- If your code stored the old refresh token after a successful rotation, the stored copy is already dead - always persist the newest one.
- Concurrent refreshes can race: two workers refresh at once and the loser's new token is invalid - serialize refreshes.
- Clock skew rarely causes 401 on refresh, but check it if re-auth also fails.
- Log token lifetimes at issuance so expiry never surprises you again.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_QOuCYNtxit-3seNcZYpNgA
