[BUG] Windows: recurring forced logouts since ~Jul 22 — .credentials.json overwritten with test-fixture content ("fixture-claude-secret-value-x")

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 27, 2026

Bug: Windows — recurring forced logouts since ~Jul 22; .credentials.json found overwritten with test-fixture content

Preflight

  • [x] Searched existing issues. Related but distinct: #80585 (macOS Keychain, concurrent-session refresh race), #80292 (multi-device ping-pong), #80085 (macOS keychain concurrency), #81512 (Cowork server-side session TTL). None cover Windows, and none report the fixture-content overwrite below.
  • [x] Single bug report.
  • [x] Latest version (CLI 2.1.220).

Environment

  • OS: Windows 11 Pro 10.0.26200
  • Claude Code CLI: 2.1.220, native installer (~/.local/bin/claude.exe, versions dir ~/.local/share/claude/versions/)
  • Claude Desktop app: 1.24012.9 (MSIX, WindowsApps), which runs its own embedded CLI 2.1.219 (%APPDATA%\Claude\claude-code\2.1.219\claude.exe)
  • Auth: subscription OAuth, stored in ~/.claude/.credentials.json (no keychain on Windows)
  • Workflow: several concurrent long-lived sessions is normal here — at capture time 4 standalone CLI sessions (--dangerously-skip-permissions, one alive since Jul 25 12:29) plus the desktop app's embedded CLI, all sharing the single ~/.claude/.credentials.json

Symptom

Since ~Jul 22, active sessions are forced to /login one to three times per day. The active session prints Not logged in · Please run /login mid-turn (in the latest case, seconds after a successful Write tool call) and all other concurrent sessions are logged out at the same time.

Logout banner occurrences found in session transcripts (timestamp = last entry in each affected session, UTC):

2026-07-22T11:17Z, 2026-07-24T19:44Z, 2026-07-25T12:15Z, 2026-07-25T16:30Z,
2026-07-25T18:40Z, 2026-07-25T22:48Z (background agent), 2026-07-26T09:50Z,
2026-07-26T18:19Z, 2026-07-27T08:07Z

Nine forced logouts in six days. Before Jul 22 there are zero occurrences in the transcript history, so this began with the 2.1.216/2.1.217-era releases — the same window as #80292 / #80585 / #80085 on macOS.

The unusual part: what the credentials file contained after the logout

Five seconds after the Jul 27 logout banner (transcript timestamp 08:07:06Z; file mtime 08:07:11Z), ~/.claude/.credentials.json was 42 bytes containing test-fixture data, not a cleared or real credential:

{"token": "fixture-claude-secret-value-x"}

That is not the production schema (claudeAiOauth / accessToken / refreshToken / expiresAt) — it looks like a unit-test fixture.

I searched exhaustively for the origin of that string on the machine. It does not exist anywhere except the credentials file itself:

  • not in any project repo (including gitignored files), any Python venv, or any test suite on disk
  • not in ~/.claude (plugins, hooks, transcripts, file-history, settings) — and Claude Code's own file-history confirms no agent session wrote the file
  • not in the CLI binaries for 2.1.218 / 2.1.219 / 2.1.220
  • not in the desktop app 1.24012.9 (app.asar, app.asar.unpacked, claude.exe, cowork-svc.exe, chrome-native-host.exe, ion-dist)
  • no hooks are configured globally, and the only project PreToolUse hook is read-only

So whatever wrote it either constructs the string at runtime or it arrived from the network and was persisted verbatim.

Hypotheses

  1. Refresh-rotation race (Windows variant of #80585): concurrent sessions share the single .credentials.json; refresh tokens are single-use; the loser of the rotation race gets an invalid_grant-class failure and the credential is clobbered, logging out every session. The Jul-22 start date and multi-session workflow match #80585/#80292 exactly — Windows just has no keychain, so the shared file is even more exposed (no OS-level locking).
  2. The fixture content specifically suggests something in the auth path can persist a non-production response body: if a refresh request ever hits a fixture/canary/staging response shaped like {"token": "..."}, the client appears to write it to disk verbatim instead of rejecting it. Worth checking server-side too — several platforms started seeing forced logouts the same week.

Impact

Multi-session workflows (background agents, several terminals, desktop + CLI) are logged out together, interrupting all in-flight work, roughly daily. On Windows there is no keychain mitigation, and the desktop app (embedded 2.1.219) and native CLI (2.1.220) — two different versions — share the same credentials file.

Repro (as far as known)

  1. Windows, subscription OAuth login.
  2. Leave 2+ claude sessions (CLI and/or desktop app) running past the access-token lifetime.
  3. Within ~24h, all sessions force /login; inspect ~/.claude/.credentials.json immediately after — in our capture it contained the fixture value above rather than a real or empty credential.

Happy to provide timestamps, transcript excerpts, or run instrumentation (e.g. a poller hashing the credentials file like the one in #80585) on request.

View original on GitHub ↗

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