v2.1.221: OAuth login always fails with 'Login expired' - credentials.json expiresAt written as 0
Description
After the auto-update from v2.1.220 to v2.1.221, /login (and claude /login / claude logout followed by a fresh browser login) always results in Claude Code immediately reporting:
Login expired · Please run /login
...even right after a brand-new, successful login.
Root cause found
Inspecting ~/.claude/.credentials.json after a fresh login shows the claudeAiOauth.expiresAt field is written as 0 (Unix epoch 0 = 1970-01-01), instead of a real future expiry timestamp in ms. Since 0 is always in the past, Claude Code treats the freshly-issued token as already expired.
This was reproduced across multiple login/logout cycles — every fresh login writes expiresAt: 0.
Ruled out:
- Clock skew: system date/time is correct (
Get-Datematches real time), though NTP sync status shows "Source: Local CMOS Clock" / "Last Successful Sync Time: unspecified" — worth double-checking on other reports, but the credentials file'sexpiresAtvalue being literally0rather than merely "in the past" points to a token-exchange bug rather than skew. - Conflicting env vars:
ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKEN,CLAUDE_CODE_OAUTH_TOKENare all unset. - Stale credentials file: deleting
.credentials.jsonand re-running/loginreproduces the sameexpiresAt: 0result.
Environment
- Claude Code version: 2.1.221 (regressed after auto-update from 2.1.220 at 2026-08-04 12:49 UTC, per
~/.claude/.last-update-result.json) - Install method: native installer,
C:\Users\<user>\.local\bin\claude.exe - OS: Windows 11 Home 10.0.26200
- Shell: PowerShell 5.1
- Timezone: GMT Standard Time
Steps to reproduce
- On an affected Windows install, run
claude /login(orclaude logoutthen log in again via browser). - Complete the browser OAuth flow successfully.
- Claude Code immediately shows
Login expired · Please run /loginfor any command. - Inspect
~/.claude/.credentials.json—claudeAiOauth.expiresAtis0.
Expected behavior
expiresAt should be set to a real future timestamp (ms since epoch) reflecting the actual token expiry, and a fresh login should not be immediately treated as expired.
Workaround
Setting ANTHROPIC_API_KEY (from console.anthropic.com) bypasses the OAuth flow and allows Claude Code to work, at the cost of billing through the API console instead of a Claude subscription.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗