[BUG] Auth session invalidated after sleep on macOS 26: background token refresh corrupts keychain credential instead of retaining old token

Status Open
Reported on v2.1.181
Maintainer reply None cached
Activity 4 comments · opened Jun 27, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

claude auth status --text intermittently reports "Not logged in" throughout the day — including during active use, not just after overnight sleep. Re-running claude auth login restores the session temporarily. Did not occur on the same account on an older macOS version.

What Should Happen?

Session should not be destroyed that I can continue using Claude without the need to re-authenticate.

When a background token refresh fails, Claude Code should retain the existing credential and retry, not invalidate the stored session.

Error Messages/Logs

security find-generic-password -s "Claude Code-credentials" 2>/dev/null | grep "mdat\|cdat

"cdat" = "20260626092437Z"   ← last login
"mdat" = "20260627043751Z"   ← credential overwritten during failed refresh → session dead

Steps to Reproduce

  1. Use Claude during the day.
  2. Notice that after some time, the system asks if using the stored entry in the Keychain Access is fine. Even by clicking Always Allow, it asks again.

Sorry, no clear steps to reproduce are available.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.181

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

The keychain entry (Claude Code-credentials) is not deleted — it still exists after the session dies. Its mdat timestamp is updated at the time the session becomes invalid, indicating Claude Code's background token refresh ran, failed, and wrote back an invalid state to the keychain instead of retaining the previously valid credential.

View original on GitHub ↗

4 Comments

brks-rssll · 2 months ago

Corroborating evidence from a separate macOS machine (Claude Code 2.1.195, Apple Silicon), which I filed as #72006 and am closing as a duplicate of this.

The background daemon's own auth log (~/.claude/daemon.log) shows the exact failure mode you describe — a failed proactive refresh discards the credential instead of retaining the still-valid token:

[2026-06-14T19:59:16Z] [supervisor] auth: proactive refresh succeeded
...  (~30 consecutive successes, every ~8h, for two weeks) ...
[2026-06-27T02:32:31Z] [supervisor] auth: proactive refresh succeeded      <- last good
[2026-06-28T02:18:54.730Z] [supervisor] auth: proactive refresh starting
[2026-06-28T02:18:54.786Z] [supervisor] auth: proactive refresh failed, signalling re-auth required
[2026-06-28T02:18:54.791Z] [supervisor] auth: headless daemon cannot complete OAuth — run `claude auth login` to refresh
[2026-06-28T02:18:54.791Z] [supervisor] auth: no token found, will re-check keychain every 30s

Every /login prompt across all sessions dates from that single 02:18:54Z event. A single failed refresh shouldn't transition the daemon to no token found — it should fall back to the unexpired keychain access token and retry.

Two additional data points that may refine the root cause beyond "after sleep":

  1. Not necessarily sleep-related. Earlier log lines show the credential is refreshed cross-process (auth: token still valid (cross-process refresh or not yet due)) — i.e. multiple concurrent Claude Code clients (desktop app, headless claude.exe daemon, background/forked sessions, terminal CLI) share the one keychain credential and rotate it. That shared-credential rotation looks like it can leave one client holding a refresh token that's already been rotated away, so its next refresh fails.
  1. Likely trigger here: a Pro→Max subscription upgrade. The failure landed right after upgrading the plan. Changing subscription tier (and/or signing in again on web/desktop) appears to revoke the existing OAuth refresh token server-side, so the daemon's next scheduled refresh failed with the revoked token.

Symptom contradiction worth noting: at the same time, claude auth status reported {"loggedIn": true, "subscriptionType": "max"} and the keychain held a non-expired access token, yet inference returned Not logged in and the daemon reported no token found.

brks-rssll · 2 months ago

Follow-up from the same machine (Claude Code 2.1.195, macOS) — the recovery path is also broken, not just the initial wipe.

After the failed proactive refresh put the daemon into:

[supervisor] auth: proactive refresh failed, signalling re-auth required
[supervisor] auth: headless daemon cannot complete OAuth — run `claude auth login` to refresh
[supervisor] auth: no token found, will re-check keychain every 30s

…doing exactly what it asks — claude auth logindid not recover the running daemon:

  • The re-auth succeeded and wrote a fresh, non-expired token to the keychain (claude auth statusloggedIn: true, correct subscription; security find-generic-password shows a valid expiresAt).
  • But the already-running daemon stayed wedged in the no token found state. Its advertised "re-check keychain every 30s" self-heal never picked up the new token — daemon.log shows no further auth: lines after the failure, and brand-new TUI sessions kept showing /login because they reuse that daemon.
  • Closing all TUI windows did not help (the claude.exe daemon run supervisor is a separate background process and survives).
  • The daemon also ignores SIGTERMpkill -f 'claude.exe daemon run' left it running (same PID). Only SIGKILL (kill -9 / pkill -9) stopped it; after that, a fresh claude spawned a new daemon that read the valid keychain token and logged in normally.

So the effective workaround is two steps, not one:

claude auth login                      # mint a fresh token
pkill -9 -f 'claude.exe daemon run'    # SIGTERM is ignored; SIGKILL the wedged supervisor
pkill -9 -f 'claude.exe --bg-'         # and its pre-warmed spares
claude                                 # spawns a clean daemon that reads the valid token

Suggested fixes: (1) the every 30s keychain re-check should actually re-load and clear the no token found state when a valid credential reappears; (2) the supervisor should honor SIGTERM so a normal pkill/shutdown works; (3) ideally a failed refresh shouldn't drop the daemon into a state that a successful re-auth can't recover without a manual kill.

johnfoland · 1 month ago

I had ~60 orphaned Claude Code-credentials-<hash> entries in my macOS keychain, and no plain Claude Code-credentials entry existed until I deleted everything and did a fresh OAuth login.

yoystar · 1 month ago

Try to check if it is a DNS pollution issue.