Claude in Chrome on Linux: child native host spawned by main process exits immediately on stdin EOF, bridge never established
## Environment
- OS: Ubuntu 22.04.5 LTS (Linux 6.8.0)
- Browser: Google Chrome 147.0.7727.137
- Claude Code versions tested: 2.1.119, 2.1.121, 2.1.122, 2.1.123 (all
reproduce)
- Install path:
~/.local/share/claude/versions/<ver>(symlink at
~/.local/bin/claude)
## Symptom
claude --chrome reports "Browser extension is not connected" indefinitely.
Was working previously, started failing some time after upgrades. Reinstalling
Chrome and the extension, re-logging into claude.ai, and resetting the
integration all do not fix it.
## Diagnosis (verified)
- Native messaging manifest, wrapper, allowed_origins are correct.
- Chrome ↔ native host (stdio) is healthy:
- Service worker can
chrome.runtime.connectNative('com.anthropic.claude_code_browser_extension')
and round-trip ping/pong.
- Chrome-spawned native host (parent: chrome) listens correctly on
/tmp/claude-mcp-browser-bridge-<user>/<pid>.sock.
- **Claude main process spawns its own child native host (parent: claude
main), but the child exits with code 0 immediately on stdin EOF.** Verified by
simulating spawn:
Popen([wrapper], stdin=PIPE)→ child alive- Closing stdin pipe → child exits, returncode 0
- stderr shows: `Initializing → Creating socket listener → listening →
permissions set` then exits.
- Claude main does not retry, does not fall back to connecting to the
chrome-side listening socket. lsof shows zero connections to any
/tmp/claude-mcp-browser-bridge-*/*.sock.
- Confirmed across versions 2.1.121 / 2.1.122 / 2.1.123 — same symptom, so
this is not a recent regression.
## Hypothesized root cause
The wrapper (~/.claude/chrome/chrome-native-host) execs the binary with
--chrome-native-host. This mode is designed for Chrome's native messaging
host stdio loop and exits when stdin EOFs (correct behavior for Chrome's
lifecycle). When Claude main spawns the same wrapper as a child to run a
socket-server-side helper, it appears to close stdin (or attach a stdin that
EOFs immediately), causing the child to exit before any handshake. There is no
retry / fallback path.
A separate flag --claude-in-chrome-mcp exists in the binary but the wrapper
is hard-coded to --chrome-native-host, so spawn from claude main always uses
the chrome-mode entrypoint.
## Workarounds tried (none work)
- Rebooting Chrome, reinstalling the extension, re-logging in to claude.ai
- Wiping
~/.claude/chrome/, `~/.config/google-chrome/NativeMessagingHosts/co
m.anthropic.claude_code_browser_extension.json, and/tmp/claude-mcp-browser-bridge-*/` and letting setup regenerate them
(regenerated correctly, behavior unchanged)
- Downgrading binary symlink + wrapper to 2.1.121 / 2.1.119
- Replacing the wrapper with a
tail -f /dev/nullstdin keep-alive variant
succeeds at keeping the child alive on direct testing, but Claude main
rewrites the wrapper back to its canonical content on every startup
(self-heal), so the workaround is overwritten before child spawn happens.
## Repro steps
claude --chromeon Linux (Ubuntu 22.04, Chrome 147)- Confirm Chrome-side native host is healthy via service worker DevTools
connectNative ping.
- Observe
claude --chromereports "Browser extension is not connected"
indefinitely.
pgrep -af chrome-native-hostshows ephemeral children with
parent=<claude main pid> that exit ~immediately.
lsof -p <claude main pid>shows no unix-domain connections to the socket
directory.
## Asks
- Is the child native host spawned by Claude main expected to stay alive when
its stdin is closed? If yes, the spawn path looks broken.
- Could
--chrome-native-hostmode be replaced (or guarded) to avoid the
immediate EOF exit when invoked outside Chrome's stdio loop?
- A retry / fallback to connect to chrome-side listening sockets in
/tmp/claude-mcp-browser-bridge-<user>/ would also harden this path.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗