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
- Prefix the target with http:// or https:// (lowercase), e.g. "https://example.com/path".
- Normalize the scheme to lowercase before calling the tool if the URL comes from user input.
- 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
- Default to https:// when a bare host is supplied by a user or model.
- Lowercase the scheme before calling the tool (the check is case-sensitive).
- Treat file://, ftp://, gopher:// attempts as injection signals and log them.
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
- URL cannot contain whitespace
- URL host has unmatched IPv6 brackets
- Only http:// and https:// URLs are allowed
- broker_url must start with 'mqtt://' or 'mqtts://', got: {}
- amqp_url must start with 'amqp://' or 'amqps://', got: {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b5a0675ac27432ae.
Report an issue: GitHub.