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

URL cannot be empty

Error message

URL cannot be empty

What it means

validate_url_policy is the entry gate for HTTP request targets, reached from validate_url (test surface) and validate_request_target_with_resolver (the live request path). It trims the raw URL and bails with 'URL cannot be empty' when nothing remains. This is the first and cheapest check in a chain that continues with whitespace rejection, http/https scheme enforcement, allowed_domains configuration, and host/IP policy (cloud metadata and link-local blocking). Because the input is trimmed first, a URL consisting solely of spaces, tabs, or newlines also lands here.

Source

Thrown at crates/zeroclaw-tools/src/http_request.rs:123

            nat64_prefixes: domain_guard::parse_nat64_prefixes(
                &nat64_prefixes,
                "security.nat64_prefixes",
            )?,
            config_path: Some(config_path),
            secrets_encrypt,
        })
    }

    #[cfg(test)]
    fn validate_url(&self, raw_url: &str) -> anyhow::Result<String> {
        Ok(self.validate_url_policy(raw_url)?.url)
    }

    fn validate_url_policy(&self, raw_url: &str) -> anyhow::Result<HttpRequestUrlPolicy> {
        let url = raw_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");
        }

        if self.allowed_domains.is_empty() {
            anyhow::bail!(
                "HTTP request tool is enabled but no allowed_domains are configured. Add [http_request].allowed_domains in config.toml"
            );
        }

        let host = extract_host(url)?;
        if let Ok(ip) = host.parse::<IpAddr>() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Pass a complete, explicit URL including scheme, e.g. {"url": "https://api.example.com/v1/status"}.
  2. If the URL comes from configuration, validate it at startup and fail fast with a named config error instead of at request time.
  3. Default unset env/config values to a known-good endpoint in the caller rather than forwarding an empty string.
  4. Remember the next checks in the chain: the URL must be http/https, whitespace-free, and its host must be in allowed_domains.

Example fix

// before
let args = serde_json::json!({ "url": "" }); // e.g. env var unset, forwarded as empty
// tool bails: URL cannot be empty

// after
let endpoint = std::env::var("API_URL").unwrap_or_else(|_| "https://api.example.com".into());
let args = serde_json::json!({ "url": format!("{endpoint}/v1/status") });
Defensive patterns

Strategy: validation

Validate before calling

fn build_http_args(url: &str) -> Option<serde_json::Value> {
    (!url.trim().is_empty()).then(|| serde_json::json!({ "url": url.trim() }))
}

Type guard

fn is_non_empty_http_url(raw: &str) -> bool {
    let u = raw.trim();
    !u.is_empty() && (u.starts_with("http://") || u.starts_with("https://"))
}

Try / catch

match tool_result {
    Err(e) if e.to_string().contains("URL cannot be empty") => {
        // the endpoint variable resolved to blank; fix config resolution, do not retry as-is
    }
    other => other,
}

Prevention

When it happens

Trigger: Issuing an http_request tool call with {"url": ""} or {"url": " "}; building the URL from an environment variable or config key that is unset (empty string); concatenating a base URL and path where the base is empty; forwarding a user-supplied URL field that was never filled in.

Common situations: Config templates ship with an empty endpoint placeholder that is never populated; a secrets/env loader silently substitutes missing values with empty strings; an agent constructs the target from a previous response field that was absent; trailing whitespace from a config file is trimmed into legality only when the whole value was whitespace.

Related errors


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