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 = parsedView on GitHub (pinned to 88bb9c8533)
Solutions
- Pass a concrete http(s) URL
- Check upstream: if the URL source can be empty, make it an Option and skip the call instead of forwarding ""
- 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
- Model optional URLs as Option<String>, not String::default()
- Assert non-empty inputs at the boundary where they enter your pipeline
- Fail fast on empty template/config values in tests
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
- TTS text must not be empty
- TTS text too long ({} chars, max {})
- providers.models.{profile_name}.uri must use http/https
- Custom model_provider `{prefix}:<url>` requires a URL beginn
- OpenAI Codex endpoint override cannot be empty
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/75bb7a15057b2aca.
Report an issue: GitHub.