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

Only http:// and https:// URLs are allowed

Error message

Only http:// and https:// URLs are allowed

What it means

Thrown by HttpRequestTool::validate_url_policy (crates/zeroclaw-tools/src/http_request.rs:131) when the trimmed URL does not literally start with "http://" or "https://". The tool is an allowlisted egress tool and deliberately refuses every other scheme (ftp, file, gopher, data, ws, ...). Note the check is case-sensitive on the prefix, so "HTTP://example.com" is also rejected here.

Source

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

    #[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>() {
            if domain_guard::is_known_cloud_metadata_endpoint(ip) {
                anyhow::bail!("Blocked cloud metadata host: {host}");
            }
            if domain_guard::is_cloud_metadata_ip(ip) {
                anyhow::bail!(
                    "Blocked link-local host: {host}; 169.254.0.0/16 is blocked unconditionally \
                     because cloud metadata services are hosted in that range"
                );

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Prefix the target with http:// or https:// (lowercase), e.g. "https://example.com/path".
  2. Normalize the scheme to lowercase before calling the tool if the URL comes from user input.
  3. For file access use the dedicated file tools, not http_request; for other protocols use a purpose-built client outside this tool.

Example fix

// before
let args = json!({"url": "example.com/api/v1"}); // or "HTTP://example.com"

// after
let args = json!({"url": "https://example.com/api/v1"});
Defensive patterns

Strategy: validation

Validate before calling

fn scheme_allowed(url: &str) -> bool {
    let u = url.trim().to_ascii_lowercase();
    u.starts_with("http://") || u.starts_with("https://")
}

Type guard

fn is_http_url(url: &str) -> bool {
    let u = url.trim().to_ascii_lowercase();
    u.starts_with("http://") || u.starts_with("https://")
}

Try / catch

let result = tool.execute(args).await?;
if let Some(err) = &result.error {
    if err.contains("Only http:// and https:// URLs are allowed") {
        // normalize scheme to lowercase https:// and re-submit
    }
}

Prevention

When it happens

Trigger: Passing a bare host such as "example.com/path" with no scheme; passing ftp://, file:///etc/passwd, or data: URLs; passing "HTTP://" or "HTTPS://" with uppercase scheme letters (case-sensitive starts_with); passing a URL with leading whitespace already trimmed but scheme misspelled as "http:/" (single slash).

Common situations: Users or models pasting a domain without a scheme; SSRF-probing attempts with file:// or gopher://; template engines that uppercase the scheme; typos when hand-assembling URLs.

Related errors


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