anthropics / anthropics/claude-code
[Bug] Future-dated OAuth refresh lock blocks token refresh indefinitely
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 147k
- Forks
- 24k
- PR merge metrics
- PR metrics pending
Description
Bug Description
[bug] A future-dated ~/.claude/.oauth_refresh.lock never expires and blocks all token refresh
What happened: On a host that corrects its clock backward ~4h shortly after boot, a token refresh started before the correction and failed. It left ~/.claude/.oauth_refresh.lock with an mtime ~4h in the future. Lock staleness looks age-based, so now - mtime stayed negative and the lock never expired: the daemon's 30s keychain re-check never recovered, and every claude invocation returned "Failed to refresh OAuth token: another Claude Code process is refreshing it or exited mid-refresh. This is usually transient; retry in a minute..." with no other Claude Code process running. rmdir on the empty lock directory restored refresh immediately using the same stored credentials. Expected: a lock whose mtime is in the future is treated as invalid, or the lock is keyed on pid/boot-id rather than age.
Two things made this hard to diagnose: ~/.claude/daemon-auth-status.json still read {"status":"auth_required"} after the refresh succeeded (it is not rewritten on recovery), and claude auth status reported loggedIn: true throughout, since it reads the stored credential without checking expiry.
What the user said: "Why is [the containerized session] logged out again? Is it DNS, firewall, or other similar issues again?"
Repro: Have the system clock step backward while a token refresh is in flight (container or VM boot, NTP correction, host resume). The lock is then stamped in the future and blocks every refresh until real time passes that mtime.
Evidence: CLI 2.1.267. Three cold boots, same daemon.log signature: refresh failed 2.510s / 2.535s / 2.512s after starting, each followed by "auth: headless daemon cannot complete OAuth". Lock mtime observed ~4h ahead of wall clock.
Cause: Verified in-session — removing the future-dated lock restored refresh with unchanged credentials, so the refresh token was valid the whole time and the lock was the only blocker.
Environment Info
- Platform: linux
- Terminal: kitty
- Version: 2.1.267
- Feedback ID: f89c1aac-f29d-4b5e-8bee-3c382e5acd9e
Errors
[]
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the daemon's OAuth refresh lock handling and the staleness check for ~/.claude/.oauth_refresh.lock. Reproduce a future-dated lock after a backward clock step, then verify refresh recovers without manually removing the lock; also check the observed daemon-auth-status.json and auth status behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- authentication, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100