claude-code - 💡(How to fix) Fix Claude in Chrome on Linux: child native host spawned by main process exits immediately on stdin EOF, bridge never established [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#54526Fetched 2026-04-30 06:43:12
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
labeled ×4cross-referenced ×1

Root Cause

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.

Fix Action

Fix / Workaround

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.
RAW_BUFFERClick to expand / collapse

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.
  3. Chrome-spawned native host (parent: chrome) listens correctly on /tmp/claude-mcp-browser-bridge-<user>/<pid>.sock.
  4. 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.
  5. 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.
  6. 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.
  3. Observe claude --chrome reports "Browser extension is not connected" indefinitely.
  4. pgrep -af chrome-native-host shows ephemeral children with parent=<claude main pid> that exit ~immediately.
  5. 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.

extent analysis

TL;DR

The issue can be fixed by modifying the wrapper to use the --claude-in-chrome-mcp flag instead of --chrome-native-host to prevent the child native host from exiting immediately when its stdin is closed.

Guidance

  • Verify that the child native host is indeed exiting due to stdin closure by running the wrapper with --claude-in-chrome-mcp flag and checking if it stays alive.
  • Modify the wrapper to use the --claude-in-chrome-mcp flag instead of --chrome-native-host to prevent the child native host from exiting immediately.
  • Consider adding a retry/fallback mechanism to connect to chrome-side listening sockets in /tmp/claude-mcp-browser-bridge-<user>/ to harden the connection path.
  • Investigate why Claude main rewrites the wrapper back to its canonical content on every startup, and consider modifying this behavior to allow for customizations.

Example

No code snippet is provided as the issue does not require a specific code change, but rather a modification to the wrapper script.

Notes

The issue seems to be related to the wrapper script using the wrong flag, causing the child native host to exit immediately. Modifying the wrapper to use the correct flag should fix the issue. However, the root cause of why Claude main rewrites the wrapper on every startup is still unknown and may require further investigation.

Recommendation

Apply the workaround by modifying the wrapper to use the --claude-in-chrome-mcp flag instead of --chrome-native-host. This should prevent the child native host from exiting immediately and allow the connection to be established.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix Claude in Chrome on Linux: child native host spawned by main process exits immediately on stdin EOF, bridge never established [1 participants]