zeroclaw-labs/zeroclaw · error · anyhow::Error

Requested text browser '{browser}' is not installed

Error message

Requested text browser '{browser}' is not installed

What it means

After a requested browser name passes the supported-list check, resolve_browser verifies the binary exists by running `which <browser>` and requiring a successful exit. If the command fails or the process cannot spawn, the tool concludes the browser is not installed and bails.

Source

Thrown at crates/zeroclaw-tools/src/text_browser.rs:159

    async fn resolve_browser(&self, requested: Option<&str>) -> anyhow::Result<String> {
        // If the caller explicitly requested a browser via the tool parameter, use it.
        if let Some(browser) = requested {
            let browser = browser.trim().to_lowercase();
            if !SUPPORTED_BROWSERS.contains(&browser.as_str()) {
                anyhow::bail!(
                    "Unsupported text browser '{browser}'. Supported: {}",
                    SUPPORTED_BROWSERS.join(", ")
                );
            }
            // Verify it's installed
            let installed = tokio::process::Command::new("which")
                .arg(&browser)
                .output()
                .await
                .map(|o| o.status.success())
                .unwrap_or(false);
            if !installed {
                anyhow::bail!("Requested text browser '{browser}' is not installed");
            }
            return Ok(browser);
        }

        // If a preferred browser is set in config, try it first.
        if let Some(ref preferred) = self.preferred_browser {
            let preferred = preferred.trim().to_lowercase();
            if SUPPORTED_BROWSERS.contains(&preferred.as_str()) {
                let installed = tokio::process::Command::new("which")
                    .arg(&preferred)
                    .output()
                    .await
                    .map(|o| o.status.success())
                    .unwrap_or(false);
                if installed {
                    return Ok(preferred);
                }
                ::zeroclaw_log::record!(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Install the browser: Debian/Ubuntu `apt-get install -y lynx`, Alpine `apk add lynx`, macOS `brew install lynx` / `brew install w3m`
  2. Verify with `which lynx` in the same environment the tool runs in
  3. Or omit the browser parameter so auto-detection picks whichever of the three is installed

Example fix

# before: container without a text browser
$ docker run myagent zeroclaw ... browser=w3m   # bails
# after
$ docker run myagent sh -c "apk add w3m && zeroclaw ... browser=w3m"
Defensive patterns

Strategy: fallback

Validate before calling

// Cheap preflight: check the binary resolves before invoking the tool
async fn browser_installed(name: &str) -> bool {
    tokio::process::Command::new("which").arg(name).output()
        .await.map(|o| o.status.success()).unwrap_or(false)
}

Try / catch

Err(e) if e.to_string().contains("is not installed") => {
    // fall back: retry with browser omitted so auto-detect picks an installed one
}

Prevention

When it happens

Trigger: Requesting "w3m" or "lynx" on a minimal container, fresh server, or CI image where none of the three browsers were installed; PATH not including the binary's directory under custom shells (NixOS, homebrew on Linux).

Common situations: Docker/minimal images used for agent runtimes; macOS machines without lynx/w3m preinstalled; environments where `which` itself is absent (some minimal images ship `command -v` only), which the unwrap_or(false) treats as not installed.

Related errors


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