Claude in Chrome: WebSocket bridge pairing fails silently on Windows despite all prerequisites met
Environment
- OS: Windows 11 Pro 10.0.26200
- Claude Code: v2.1.39
- Chrome Extension: v1.0.49 (ID: fcoeoabgfenejglbffodgkkbkcdhcgfn)
- Chrome: latest stable
- Account: Claude Max subscription
Problem
mcp__claude-in-chrome__tabs_context_mcp always returns "Browser extension is not connected" on Windows. After 6 debugging sessions, the root cause is that the WebSocket bridge (wss://bridge.claudeusercontent.com) never pairs the desktop (Claude Code) and Chrome extension sides, even though both connect successfully.
What Works
- Chrome extension connects to
wss://bridge.claudeusercontent.com/chrome/{accountUuid}and receives{"type":"waiting"} - Claude Code main process establishes 3+ bridge connections to
160.79.104.10:443 - Extension OAuth token is valid, feature flag
chrome_ext_bridge_enabledis enabled - Account UUID resolves correctly via
/api/oauth/profile - Extension sends correct connect message:
{"type":"connect","client_type":"chrome-extension","device_id":"...","os_platform":"Windows","oauth_token":"..."}
What Fails
The bridge server returns {"type":"stats","desktopConnected":true,"addinCount":0} — it never counts the Chrome extension as a valid addin. Both sides remain in "waiting" state indefinitely. No "paired" or "peer_connected" messages are ever received.
Root Causes Found
1. oauthAccount.accountUuid never populated in Claude Code
B$().oauthAccount?.accountUuid (used as user_id in the bridge connect message) is only populated during token refresh via Zn$(). If the token in .credentials.json hasn't expired, refresh never triggers, and getUserId() returns undefined. The bridge can't match a desktop with user_id: undefined to a Chrome extension at /chrome/{uuid}.
Setting expiresAt: 0 in .credentials.json and restarting with claude --chrome causes the new session to make 25+ bridge connections (aggressive reconnect loop) but pairing still doesn't occur. The oauthAccount is stored in runtime memory only — never written to the credentials file.
2. Extension's ir() fails silently on startup (feature flag race)
The extension's bridge init function ir() calls isFeatureEnabledAsync("chrome_ext_bridge_enabled") during startup. On a fresh extension load, the feature cache in chrome.storage.local is empty and there's no token yet to fetch flags from the API. The feature check returns false, nr() returns undefined, and ir() bails silently — no error, no retry.
3. Bun crash on Windows native messaging (known)
claude.exe --chrome-native-host crashes immediately when stdin is a pipe (Chrome's native messaging protocol). Bun v1.3.9-canary.51 panics with internal assertion failure. This blocks the native messaging fallback path entirely.
Crash report: https://bun.report/1.3.9/e_2d5628dbEkgggC+s1oU+vvkOu8t/uBysi5uB__qt24tCA0eNrzzCtJLcpLzFFILC5OLSrJzM9TSEvMzCktSgUAiSkKPg
4. No fallback from bridge to named pipes
lxE() always returns a bridge URL, making bridgeConfig always truthy in the factory function GaI(). The named pipe path (getSocketPaths / oOI()) never executes. When the bridge fails, there's no fallback.
Suggested Fixes
- Populate
accountUuideagerly — callZn$()on startup, not only during token refresh - Add retry logic to
ir()— retry after feature cache loads and token becomes available - Fix Bun stdin crash on Windows — or use Node.js for the native host
- Add bridge-to-pipe fallback — if bridge pairing fails after N seconds, fall back to named pipes
- Write
oauthAccountto credentials file — so it survives process restarts without requiring token refresh
Related Issues
- #23104 (Chrome extension not connecting)
- #23218 (Windows native host issues)
- #21300 (Chrome integration)
- #24006 (Bridge connection issues)
- #24199 (Pipe path mismatch — fixed in v2.1.39 but irrelevant due to bridge)
Reproduction
- Install Claude Code v2.1.39 on Windows 11
- Install Claude in Chrome extension v1.0.49
- Run
claude --chrome - Extension shows as installed/enabled in
chrome://extensions - Any Chrome MCP tool returns "Browser extension is not connected"
Diagnostic Data
Debug extension with 18 [BRIDGE-DBG] log markers available at ~/.claude/chrome/extension-debug/ — documents the full ir() flow and confirms all prerequisites pass except pairing.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗