zeroclaw-labs/zeroclaw · error

Failed to open URL with default browser launcher: {primary_e

Error message

Failed to open URL with default browser launcher: {primary_error}. Brave compatibility fallback also failed: {brave_error}

What it means

On macOS, browser_open first tries the system launcher `open <url>`; on failure it falls back to `open -a 'Brave Browser'` and `open -a 'Brave'`. If the primary launcher and both Brave fallbacks fail — non-zero exit, binary not runnable, or the 10-second launcher timeout — this combined error reports the primary and last fallback errors. Linux and Windows have parallel chains (xdg-open/gio/sensible-browser/brave on Linux).

Source

Thrown at crates/zeroclaw-tools/src/browser_open.rs:206

        let mut command = tokio::process::Command::new("open");
        command.arg(url);
        let primary_error = match run_browser_launcher(command, "open").await {
            Ok(()) => return Ok(()),
            Err(error) => error,
        };

        // TODO(compat): remove Brave fallback after default-browser launch has been stable across macOS environments.
        let mut brave_error = String::new();
        for app in ["Brave Browser", "Brave"] {
            let mut command = tokio::process::Command::new("open");
            command.arg("-a").arg(app).arg(url);
            match run_browser_launcher(command, &format!("open -a '{app}'")).await {
                Ok(()) => return Ok(()),
                Err(error) => brave_error = error,
            }
        }

        anyhow::bail!(
            "Failed to open URL with default browser launcher: {primary_error}. Brave compatibility fallback also failed: {brave_error}"
        );
    }

    #[cfg(target_os = "linux")]
    {
        let mut last_error = String::new();
        for cmd in [
            "xdg-open",
            "gio",
            "sensible-browser",
            "brave-browser",
            "brave",
        ] {
            let mut command = tokio::process::Command::new(cmd);
            if cmd == "gio" {
                command.arg("open");
            }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Verify a GUI session exists and `open https://example.com` (or xdg-open on Linux) works from the same user and terminal
  2. Set a working default browser — the Brave fallback only helps if Brave is actually installed
  3. On headless/CI hosts, disable browser_open and use scraping/automation tools instead; it fundamentally requires a desktop
Defensive patterns

Strategy: fallback

Validate before calling

// gate the tool on launcher availability before enabling it
#[cfg(target_os = "linux")]
let can_launch = which::which("xdg-open").is_some() || which::which("gio").is_some();
#[cfg(target_os = "macos")]
let can_launch = which::which("open").is_some();
if !can_launch {
    return Err("no system browser launcher available; disable browser_open".into());
}

Try / catch

match open_tool.execute(args).await {
    Ok(res) if res.success => { /* opened */ }
    Ok(res) => {
        if res.error.as_deref().unwrap_or_default().contains("Failed to open URL with default browser launcher") {
            // fall back: log the URL for the user, or surface it in chat for manual opening
        }
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling browser_open on a headless macOS machine or over SSH with no GUI session (open exits non-zero); default browser misconfigured with Brave not installed; launcher binaries missing from PATH; a launcher hanging past the 10-second deadline.

Common situations: Agents running as daemons or in CI where no user GUI session exists; corporate machines where the default browser was removed; sandboxed environments without /usr/bin/open or xdg-open available.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/902a7a6a4c625fbb. Report an issue: GitHub.