OAuth refresh token never used — forces re-login every 8 hours

Resolved 💬 3 comments Opened Mar 5, 2026 by tydds Closed Mar 9, 2026

Description

Claude Code CLI stores both an accessToken and refreshToken in the macOS Keychain (Claude Code-credentials), but the refresh token is never used. When the 8-hour access token expires, the CLI invalidates the cached credentials instead of refreshing, forcing the user to /login again.

Steps to Reproduce

  1. /login to authenticate (creates keychain entry with accessToken + refreshToken)
  2. Keep a session running (interactive or via -c resume) for longer than 8 hours
  3. Background client_data poll to api.anthropic.com/api/oauth/claude_cli/client_data returns HTTP 401: "OAuth token has expired"
  4. Next user prompt fails with "Could not resolve authentication method"
  5. User must /login again

Evidence

Observed 5 times across versions 2.1.62–2.1.69. The pattern is consistent:

| Login (UTC) | Next 401 (UTC) | Apparent Lifetime |
|---|---|---|
| Feb 22 05:26 | Feb 27 21:36 | ~5.7 days (machine sleeping) |
| Feb 27 22:27 | Feb 28 06:51 | ~8.4 hours |
| Feb 28 14:21 | Mar 02 06:46 | ~1.7 days (machine sleeping) |
| Mar 03 02:32 | Mar 05 08:59 | ~2.3 days (machine sleeping) |

The access token's expiresAt field confirms an 8-hour lifetime. Longer apparent lifetimes are due to the machine being asleep (no poll = no 401 detection).

Debug logs show zero refresh activity — the word "refresh" only appears in the 401 error message itself. The [API:auth] OAuth token check path appears to only validate the access token's presence, not trigger a refresh flow.

Additionally, every /login logs Error: Failed to delete keychain entry (non-fatal) before auth_success, suggesting the token was already cleared by the invalidation step.

Expected Behavior

When the client_data poll receives a 401, the CLI should use the stored refresh token to obtain a new access token transparently, without requiring user interaction.

Workaround

Short-lived claude -p invocations (e.g., from automation scripts) are unaffected because each process reads the current keychain credentials at startup. Only long-running interactive sessions or -c resumed sessions hit this bug.

Environment

  • macOS 15 (Darwin 25.3.0)
  • Claude Code 2.1.69 (also reproduced on 2.1.62–2.1.68)
  • Anthropic Max subscription
  • Auth method: OAuth (not API key)

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗