Skip to content

Juggler pipe becomes unresponsive while browser process stays alive (dead pipe state) #719

Description

@dyiapanis

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

  1. Launch Camoufox with -juggler-pipe flag
  2. Send Browser.enable — works, Juggler initializes
  3. Create contexts via Browser.createBrowserContext — works
  4. Create pages via Browser.newPage — works for normal sites
  5. Navigate to a Cloudflare-protected page (e.g. https://simplywall.st/dashboard)
  6. After some navigations, Browser.newPage starts hanging — no response, no error
  7. Browser.enable was already called, the process is alive, but no Juggler command completes
  8. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions