v2.1.221: OAuth login always fails with 'Login expired' - credentials.json expiresAt written as 0

Status Open
Reported on v2.1.221
Maintainer reply None cached
Activity 1 comment · opened Aug 4, 2026

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-Date matches 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's expiresAt value being literally 0 rather 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_TOKEN are all unset.
  • Stale credentials file: deleting .credentials.json and re-running /login reproduces the same expiresAt: 0 result.

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

  1. On an affected Windows install, run claude /login (or claude logout then log in again via browser).
  2. Complete the browser OAuth flow successfully.
  3. Claude Code immediately shows Login expired · Please run /login for any command.
  4. Inspect ~/.claude/.credentials.jsonclaudeAiOauth.expiresAt is 0.

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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗