Claude in Chrome on Linux: child native host spawned by main process exits immediately on stdin EOF, bridge never established

Resolved 💬 2 comments Opened Apr 29, 2026 by kevinlia1 Closed Jun 2, 2026

## 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)

  1. Native messaging manifest, wrapper, allowed_origins are correct.
  2. Chrome ↔ native host (stdio) is healthy:
  • Service worker can

chrome.runtime.connectNative('com.anthropic.claude_code_browser_extension')
and round-trip ping/pong.

  1. Chrome-spawned native host (parent: chrome) listens correctly on

/tmp/claude-mcp-browser-bridge-<user>/<pid>.sock.

  1. **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.

  1. 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.

  1. 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/null stdin 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

  1. claude --chrome on Linux (Ubuntu 22.04, Chrome 147)
  2. Confirm Chrome-side native host is healthy via service worker DevTools

connectNative ping.

  1. Observe claude --chrome reports "Browser extension is not connected"

indefinitely.

  1. pgrep -af chrome-native-host shows ephemeral children with

parent=<claude main pid> that exit ~immediately.

  1. 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-host mode 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗