Hmbown/CodeWhale · warning

failed to launch browser command

Error message

failed to launch browser command: {err}

What it means

spawn_external_url_command builds an OS command (e.g. xdg-open/open/browser binary) to open a URL and calls .spawn(). When the OS fails to start the process — binary not found, permission denied, exec format error — the io::Error is wrapped into this anyhow error. The TUI then reports that the URL could not be opened in a browser.

Solutions

  1. Install an opener for your platform (xdg-open on Linux via xdg-utils, `open` exists on macOS) or install a browser.
  2. Check/fix the BROWSER environment variable so it names an existing executable.
  3. Verify the command exists: run `which xdg-open` (or the configured binary) in the same environment; fix PATH if missing.
  4. In code, fall back to printing the URL so the user can open it manually when spawn fails.
Defensive patterns

Strategy: fallback

Validate before calling

let cmd = opener_command(); // e.g. xdg-open / open
if which::which(&cmd).is_err() {
    eprintln!("No browser opener found; open this URL manually: {url}");
    return Ok(());
}

Type guard

fn opener_available(cmd: &str) -> bool { std::process::Command::new(cmd).arg("--version").output().is_ok() }

Try / catch

match spawn_external_url_command(&cmd, &url) {
    Ok(()) => {},
    Err(err) => {
        log::warn!("{err}");
        print!("Open manually: {url}"); // graceful fallback
    }
}

Prevention

When it happens

Trigger: Calling spawn_external_url_command (via the URL opener path, e.g. from a test or the external_url_launcher) when the configured browser/open command does not exist on PATH, is not executable, or the OS refuses to fork/exec.

Common situations: Minimal Docker/container images or headless servers with no xdg-open, sensible-browser, or open installed; a BROWSER env var pointing at a nonexistent binary; PATH not set in the launch environment; macOS on Linux or vice versa.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/38873836223f924a. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/ui.rs:940

pub(crate) mod frame;
mod overlays;
mod provider_routes;
mod session_state;

pub(crate) use frame::*;
pub(crate) use overlays::*;
pub(crate) use provider_routes::*;
pub(crate) use session_state::*;

#[cfg(test)]
fn spawn_external_url_command(mut command: Command) -> Result<()> {
    command
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .map(|_| ())
        .map_err(|err| anyhow::anyhow!("failed to launch browser command: {err}"))
}

async fn execute_command_input(
    terminal: &mut AppTerminal,
    app: &mut App,
    engine_handle: &mut EngineHandle,
    task_manager: &SharedTaskManager,
    config: &mut Config,
    input: &str,
) -> Result<bool> {
    let _ = app.note_manual_command_for_tip(input);
    if let Some(parsed_index) = parse_queue_send_command(input) {
        match parsed_index {
            Ok(index) => {
                send_queued_message_at_index_now(app, config, engine_handle, index).await?;
            }
            Err(message) => {
                app.status_message = Some(message);

View on GitHub (pinned to 433685b202)