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
- Pass a complete, explicit URL including scheme, e.g. {"url": "https://api.example.com/v1/status"}.
- If the URL comes from configuration, validate it at startup and fail fast with a named config error instead of at request time.
- Default unset env/config values to a known-good endpoint in the caller rather than forwarding an empty string.
- 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
- Validate required endpoint config at startup (non-empty, http/https, host in allowed_domains) so failures surface at boot, not mid-request.
- Avoid silent empty-string fallbacks when env vars or config keys are missing; fail with the missing key's name.
- Trim URLs at the boundary; whitespace-only URLs are rejected by this same check after trimming.
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
- URL cannot contain whitespace
- Only http:// and https:// URLs are allowed
- URL host has unmatched IPv6 brackets
- providers.models.{profile_name}.uri must use http/https
- Custom model_provider `{prefix}:<url>` requires a URL beginn
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/d2044cfb381b507e.
Report an issue: GitHub.