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)
Status Open
Reported on v2.1.263
Maintainer reply None cached
Workaround ✓ Mentioned in description ↑
Activity 1 comment · opened Sep 7, 2026
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-code2.1.263 (bundled native binary 2.1.263) - CLI
claude2.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_credmanflag 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:
- Panel login (
{"type":"login","method":"claudeai"}) delegates to a spawned SDKclaude.exesession (loginViaQuery→claudeAuthenticate→claudeOAuthWaitForCompletion). - After completing the browser flow, a fresh valid token was written to
.credentials.json(verified: mtime updated seconds after the browser redirect, newexpiresAt~8h out, and the extension's own bundlednative-binary\claude.exerun headless from a terminal with that file authenticated fine and answered a-pprompt). - Yet the panel surfaced the "OAuth session expired and could not be refreshed" error (which is generated inside the CLI binary — the
authentication_failed/sYerror path) right as/after login completed.
Contributing factors observed:
- Multiple concurrent
claude.exeprocesses 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. /logoutin the panel vs theClaude Code: Logoutcommand 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 PaletteClaude Code: Logoutproduced "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
- 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.
- In the login completion path, don't surface a refresh failure from a different/stale process while
claudeOAuthWaitForCompletionhas actually succeeded; re-check the credential store after the exchange before reporting failure. - Serialize token refresh across processes on Windows (lockfile around
.credentials.jsonrefresh) to avoid rotation races between the extension's spawned processes, terminal CLI sessions, and the Desktop app. - Make panel
/logoutandClaude Code: Logoutclear the same state, or remove one.
Workaround that ended the loop
- Command Palette →
Claude Code: Logout(not/logoutin the panel) — this actually cleared auth state ("No authentication found"). - Sign in once from the panel (browser flow) — writes a fresh token family to
.credentials.json. - Fully close VS Code so no stale
claude.exehelpers survive, reopen — panel is authenticated; ignore the stale error bubble at the end of the resumed conversation.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗