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
- Verify a GUI session exists and `open https://example.com` (or xdg-open on Linux) works from the same user and terminal
- Set a working default browser — the Brave fallback only helps if Brave is actually installed
- 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
- Do not enable browser_open on headless hosts or CI runners
- Verify `open <url>` / `xdg-open <url>` works from the same user session before deploying
- Surface the URL to the user as a clickable fallback when launching fails
- Set a valid default browser on machines where the tool runs
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
- Invalid iMessage target: must be a phone number (+1234567890
- iMessage send failed: {stderr}
- Messages database not found at {}. Ensure Messages.app is se
- launchctl start failed: {}
- Docker runtime environment passthrough key must be a variabl
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/902a7a6a4c625fbb.
Report an issue: GitHub.