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

URL cannot be empty

Error message

URL cannot be empty

What it means

All text_browser URL validation paths (validate_url, validate_url_with_dns_check, validate_url_for_execute) funnel into validate_text_browser_url, whose first check after trimming is that the URL is non-empty. An empty or whitespace-only url parameter bails immediately with this message.

Source

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

        })
    }

    /// Build the command arguments for the selected browser with `-dump` flag.
    fn build_dump_args(_browser: &str, url: &str) -> Vec<String> {
        // All supported browsers (lynx, links, w3m) use the same `-dump` flag
        vec!["-dump".to_string(), url.to_string()]
    }
}

fn validate_text_browser_url(
    url: &str,
    allowed_private_hosts: &[String],
    validate_dns: impl FnOnce(&str, bool) -> anyhow::Result<()>,
) -> anyhow::Result<String> {
    let url = url.trim();

    if url.is_empty() {
        anyhow::bail!("URL cannot be empty");
    }

    if url.chars().any(char::is_whitespace) {
        anyhow::bail!("URL cannot contain whitespace");
    }

    if !url.starts_with("http://") && !url.starts_with("https://") {
        anyhow::bail!("Only http:// and https:// URLs are allowed");
    }

    let parsed = reqwest::Url::parse(url)
        .map_err(|e| anyhow::Error::msg(format!("Invalid URL format: {e}")))?;

    if !parsed.username().is_empty() || parsed.password().is_some() {
        anyhow::bail!("URL userinfo is not allowed");
    }

    let host_str = parsed

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Pass a concrete http(s) URL
  2. Check upstream: if the URL source can be empty, make it an Option and skip the call instead of forwarding ""
  3. Add a preflight trim+is_empty assertion in the caller

Example fix

// before
let url = config.get("url").unwrap_or_default();
tool.execute(json!({"url": url, "browser": "lynx"})).await?;
// after
let Some(url) = config.get("url") else { return Ok(()) };
if url.trim().is_empty() { return Ok(()) }
tool.execute(json!({"url": url.trim(), "browser": "lynx"})).await?;
Defensive patterns

Strategy: validation

Validate before calling

let url = raw.trim();
if url.is_empty() { /* skip or prompt; never call the tool */ }

Type guard

fn is_nonempty_url(u: &str) -> bool { !u.trim().is_empty() }

Try / catch

Err(e) if e.to_string() == "URL cannot be empty" => {
    // upstream produced no URL: log and skip this item rather than retry
}

Prevention

When it happens

Trigger: Calling the tool with url "" or " "; passing a variable that was never populated (empty string from a failed upstream lookup); template substitution that produced nothing.

Common situations: Pipeline stages where the URL comes from a previous step (RSS parse, config lookup) that silently returned an empty string; defaults declared as "" instead of Option.

Related errors


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