Describe the bug
When the browser navigates to certain pages (e.g. Cloudflare-protected sites, heavy SPAs), the Juggler pipe stops responding to commands — Browser.newPage, Page.navigate, and Runtime.evaluate all hang indefinitely. The browser process stays alive (/proc/PID exists, browser_alive: true) but no Juggler command completes.
The process does not crash — there is no exit signal, no error on stderr, no Browser.disconnected event. The pipe is simply silent. This is a dead pipe state: the process is alive but the Juggler protocol handler is unresponsive.
Environment
- Camoufox: v152.0.4-beta.28
- OS: Debian bookworm (Docker container)
- Launcher: direct Juggler pipe (not via Playwright/camoufox-js Python wrapper)
- Our server: a Rust HTTP server that drives Camoufox via Juggler pipes (fd 3/4)
To Reproduce
- Launch Camoufox with
-juggler-pipe flag
- Send
Browser.enable — works, Juggler initializes
- Create contexts via
Browser.createBrowserContext — works
- Create pages via
Browser.newPage — works for normal sites
- Navigate to a Cloudflare-protected page (e.g.
https://simplywall.st/dashboard)
- After some navigations,
Browser.newPage starts hanging — no response, no error
Browser.enable was already called, the process is alive, but no Juggler command completes
- The only recovery is killing and relaunching the process
Expected behavior
The Juggler protocol should either:
- Continue responding to
Browser.newPage and other commands even when a page's JS is heavy (the protocol handler runs on a separate thread from the page's JS engine)
- Emit a
Browser.disconnected or error event when the pipe becomes unresponsive
- Provide a protocol-level heartbeat/ping that can detect a dead pipe
Actual behavior
The process stays alive indefinitely. The Juggler pipe handler stops responding to all commands. No error is emitted. There is no way to detect the dead pipe from the protocol level — the only signal is a timeout on send.
Impact
We've implemented a workaround in our server: a dead_pipe flag set when Session::send times out, with a crash-recovery monitor that restarts the browser. This works but is a server-side detection of a problem that ideally shouldn't happen — the Juggler protocol handler should remain responsive even when a page's main thread is blocked.
Related issues
Suggestion
Could the Juggler protocol handler run on a separate thread from the page's JS event loop? Or could a protocol-level ping (Browser.ping or similar) be added that always responds regardless of page state? This would let automation servers detect a dead pipe without relying on send timeouts.
Describe the bug
When the browser navigates to certain pages (e.g. Cloudflare-protected sites, heavy SPAs), the Juggler pipe stops responding to commands —
Browser.newPage,Page.navigate, andRuntime.evaluateall hang indefinitely. The browser process stays alive (/proc/PIDexists,browser_alive: true) but no Juggler command completes.The process does not crash — there is no exit signal, no error on stderr, no
Browser.disconnectedevent. The pipe is simply silent. This is a dead pipe state: the process is alive but the Juggler protocol handler is unresponsive.Environment
To Reproduce
-juggler-pipeflagBrowser.enable— works, Juggler initializesBrowser.createBrowserContext— worksBrowser.newPage— works for normal siteshttps://simplywall.st/dashboard)Browser.newPagestarts hanging — no response, no errorBrowser.enablewas already called, the process is alive, but no Juggler command completesExpected behavior
The Juggler protocol should either:
Browser.newPageand other commands even when a page's JS is heavy (the protocol handler runs on a separate thread from the page's JS engine)Browser.disconnectedor error event when the pipe becomes unresponsiveActual behavior
The process stays alive indefinitely. The Juggler pipe handler stops responding to all commands. No error is emitted. There is no way to detect the dead pipe from the protocol level — the only signal is a timeout on
send.Impact
We've implemented a workaround in our server: a
dead_pipeflag set whenSession::sendtimes out, with a crash-recovery monitor that restarts the browser. This works but is a server-side detection of a problem that ideally shouldn't happen — the Juggler protocol handler should remain responsive even when a page's main thread is blocked.Related issues
new_page()(may be related — concurrent newPage calls blocking)Suggestion
Could the Juggler protocol handler run on a separate thread from the page's JS event loop? Or could a protocol-level ping (
Browser.pingor similar) be added that always responds regardless of page state? This would let automation servers detect a dead pipe without relying on send timeouts.