anthropics / anthropics/claude-code
Windows: VS Code panel shows 'OAuth session expired and could not be refreshed' although re-login succeeds (stale-process refresh race + resumed session re-renders old auth error)
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
# Bug: VS Code extension appears stuck in "Failed to authenticate: OAuth session expired and could not be refreshed" even after successful re-login
## Environment
- Windows 11 Pro (10.0.26200)
- VS Code extension `anthropic.claude-code` **2.1.263** (bundled native binary 2.1.263)
- CLI `claude` 2.1.263 (native install, `~\.local\bin\claude.exe`)
- Auth: claude.ai OAuth (Team plan), credentials in `%USERPROFILE%\.claude\.credentials.json` (plaintext file store; `tengu_windows_credman` flag is off, no Credential Manager entries)
- Other Claude surfaces running on the same machine: CLI sessions in terminals, Claude Desktop app
## Symptom
The VS Code side panel showed **"Failed to authenticate: OAuth session expired and could not be refreshed"** while a concurrently running CLI session in a plain PowerShell terminal worked fine with the same `.credentials.json`. Repeated sign-outs and browser sign-ins from the panel appeared to change nothing — the panel showed the same error immediately after completing each browser OAuth flow, for over an hour.
## What log forensics showed
Digging through the exthost logs (`Claude VSCode.log`) and correlating with `.credentials.json` mtimes revealed the sign-ins were **actually succeeding** the whole time:
1. Panel login (`{"type":"login","method":"claudeai"}`) delegates to a spawned SDK `claude.exe` session (`loginViaQuery` → `claudeAuthenticate` → `claudeOAuthWaitForCompletion`).
2. After completing the browser flow, a fresh valid token was written to `.credentials.json` (verified: mtime updated seconds after the browser redirect, new `expiresAt` ~8h out, and the *extension's own bundled* `native-binary\claude.exe` run headless from a terminal with that file authenticated fine and answered a `-p` prompt).
3. Yet the panel surfaced the "OAuth session expired and could not be refreshed" error (which is generated inside the CLI binary — the `authentication_failed` / `sY` error path) right as/after login completed.
Contributing factors observed:
- **Multiple concurrent `claude.exe` processes** are spawned by the extension (config-cache loader + chat session + login helper), plus independent CLI terminal sessions and the Desktop app. With refresh-token rotation, a stale process holding a rotated-away refresh token keeps failing and its error surfaces in the panel — the binary even ships a sibling message for this ("Failed to refresh OAuth token: another Claude Code process is refreshing it or exited mid-refresh"), but what the user is shown is the misleading "session expired" variant. This looks like the Windows-file-store analog of #79685 / #72017.
- **`/logout` in the panel vs the `Claude Code: Logout` command clear different state.** The panel `/logout` + re-login path never cleared the extension-side auth state, so the extension kept reporting the stale copy ("OAuth tokens found in secure storage") while fresh logins landed in the file. Only the Command Palette `Claude Code: Logout` produced "No authentication found".
- **The final UX trap:** after everything was actually fixed (valid token on disk, extension reading it, `fetchUtilization: 200`, bootstrap ok, session hooks running), reopening VS Code **resumed the previous conversation, which re-renders the historical auth-error bubble as the newest content in the panel**. This is indistinguishable from a live auth failure and makes the user believe re-login failed again — sending any new message works fine. This alone probably explains many "re-login doesn't help" reports.
## Suggested fixes
1. Render historical/transcript error bubbles from resumed sessions visually distinct from live errors (or timestamp them), so a restored conversation can't impersonate a current auth failure.
2. In the login completion path, don't surface a refresh failure from a *different/stale* process while `claudeOAuthWaitForCompletion` has actually succeeded; re-check the credential store after the exchange before reporting failure.
3. Serialize token refresh across processes on Windows (lockfile around `.credentials.json` refresh) to avoid rotation races between the extension's spawned processes, terminal CLI sessions, and the Desktop app.
4. Make panel `/logout` and `Claude Code: Logout` clear the same state, or remove one.
## Workaround that ended the loop
1. Command Palette → `Claude Code: Logout` (not `/logout` in the panel) — this actually cleared auth state ("No authentication found").
2. Sign in once from the panel (browser flow) — writes a fresh token family to `.credentials.json`.
3. Fully close VS Code so no stale `claude.exe` helpers survive, reopen — panel is authenticated; ignore the stale error bubble at the end of the resumed conversation.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the `Claude VSCode.log` evidence and the `loginViaQuery` → `claudeAuthenticate` → `claudeOAuthWaitForCompletion` login path, comparing it with the `.credentials.json` state and resumed-panel behavior. The issue proposes four possible fixes rather than naming a single entry point or test; done would require selecting and validating one scoped fix for stale refresh errors, logout state, or historical auth-error rendering.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, vscode
- Domain
- authentication, desktop
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100