Skip to content

Cold daemon against a windowless Chrome hangs 45s, then misreports as an unclicked "Allow remote debugging?" popup #554

Description

@illmethinks0

Summary

When the daemon starts cold against a Chrome that has zero open windows (e.g. Chrome launched with --no-startup-window), the browser-endpoint WS handshake stalls for the full 45s timeout and then fails with:

RuntimeError: permission-blocked: wait for the user to click Allow in the Chrome permission popup before retrying.

No popup is ever shown, because Chrome has no window to render one in. The user is told to click something that does not exist, and there is nothing in the message pointing at the actual cause.

Opening any Chrome window makes the identical cold start succeed immediately.

Environment

  • browser-harness 0.1.7 (git, editable install via uv)
  • Google Chrome 150.0.7871.184
  • macOS 26.5.2 (Apple Silicon)
  • Local Chrome mode (no BU_CDP_WS / BU_CDP_URL)
  • devtools.remote_debugging.user-enabled = True in Local State, DevToolsActivePort live on 9222
  • remote_debugging_user_enabled() returns True

Reproduction

Chrome running with --no-startup-window and no windows open:

osascript -e 'tell application "System Events" to tell process "Google Chrome" to return count of windows'
# 0

pkill -f browser_harness.daemon
browser-harness <<'PY'
print(page_info())
PY

Actual (deterministic, reproduced on consecutive runs):

browser-harness: Chrome is asking "Allow remote debugging?" — click Allow to continue.
RuntimeError: permission-blocked: wait for the user to click Allow in the Chrome permission popup before retrying.

Daemon log:

connecting to ws://127.0.0.1:9222/devtools/browser/dddfa87e-...
handshake-wait: if Chrome shows an 'Allow remote debugging?' popup, click Allow
fatal: permission-blocked: Chrome's 'Allow remote debugging?' popup was not accepted within 45s -- wait for the user to click Allow, then retry

Now open a single Chrome window and repeat the same cold start:

# open any window, then
pkill -f browser_harness.daemon
browser-harness <<'PY'
print(page_info())
PY
# {'url': 'about:blank', 'title': '', 'w': 1440, 'h': 713, ...}

Succeeds. Only variable changed is window count 0 -> 1.

Why the message is misleading

Confirmed there is no popup to click in the failing case: Chrome's AX tree reports count of windows = 0, and a full AXMenuBar-only tree with no dialog, sheet, or bubble anywhere in the process.

The classification in admin.py treats a handshake-wait timeout as proof that an Allow popup was shown and ignored:

if local and msg.startswith("handshake-wait"):
    restart_daemon(name)
    raise RuntimeError(
        "permission-blocked: Chrome's Allow popup was not clicked in time -- ..."
    )

A stalled handshake has at least two causes — an unclicked popup, and a windowless Chrome — and they are reported identically. The remote_debugging_user_enabled() branch does not disambiguate either: it returns True here (toggle on, port live), which routes to the same "click Allow" message.

Worth noting the daemon already handles the no-page case correctly once connectedno real pages found, created about:blank — so this is specifically about the handshake stalling before it gets that far.

Secondary: false-positive hint on successful runs

The speculative hint is printed after 2s of handshake-wait even when the run then succeeds with no popup involved (the window-open case above prints it and still returns a valid page_info). It trains users to ignore a message that is sometimes real.

Suggested fix

Two options, not mutually exclusive:

  1. Before attributing a handshake-wait timeout to the popup, check whether Chrome has any window / page target, and emit a distinct error when it does not — something like chrome has no open window; open a window (or launch without --no-startup-window) and retry.
  2. Failing a reliable window check, widen the message to name both causes rather than asserting the popup one.

Happy to send a PR if you have a preference on approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions