zeroclaw-labs/zeroclaw · error
URL must include a host
Error message
URL must include a host
What it means
extract_host strips the http:// or https:// prefix and takes everything before the first '/', '?', or '#' as the authority (browser_open.rs:289-298). If that component is empty, the URL names no host at all and this bail fires (browser_open.rs:300-302). It is the earliest structural host check in browser_open's URL validation.
Source
Thrown at crates/zeroclaw-tools/src/browser_open.rs:301
.with_attrs(::serde_json::json!({"url": url})),
"browser_open: unsupported URL scheme rejected"
);
anyhow::Error::msg("Only http:// or https:// URLs are allowed")
})?;
let authority = rest.split(['/', '?', '#']).next().ok_or_else(|| {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"url": url})),
"browser_open: invalid URL"
);
anyhow::Error::msg("Invalid URL")
})?;
if authority.is_empty() {
anyhow::bail!("URL must include a host");
}
if authority.contains('@') {
anyhow::bail!("URL userinfo is not allowed");
}
if authority.starts_with('[') {
anyhow::bail!("IPv6 hosts are not supported in browser_open");
}
let host = authority
.split(':')
.next()
.unwrap_or_default()
.trim()
.trim_end_matches('.')
.to_lowercase();
View on GitHub (pinned to 88bb9c8533)
Solutions
- Fix the caller to include a real host: 'https://example.com/path' instead of 'https:///path'.
- If the URL is built from parts, assert the host component is non-empty before formatting.
- Check the template/variable that produced the URL for an unset or empty hostname.
Example fix
// before
let url = format!("https://{}/report", maybe_empty_host);
// after
anyhow::ensure!(!maybe_empty_host.is_empty(), "host is required");
let url = format!("https://{maybe_empty_host}/report"); Defensive patterns
Strategy: validation
Validate before calling
fn has_url_host(url: &str) -> bool {
let Some(rest) = url
.strip_prefix("https://")
.or_else(|| url.strip_prefix("http://"))
else {
return false;
};
let authority = rest.split(['/', '?', '#']).next().unwrap_or("");
!authority.is_empty()
} Prevention
- Validate URLs have a non-empty host before handing them to browser_open.
- Ensure! host variables are non-empty before format!-ing URLs.
- Prefer the url::Url crate for construction; it rejects scheme-only URLs at parse time.
When it happens
Trigger: Passing a URL where nothing follows the scheme before the next delimiter: 'https://' alone, 'https:///path' (empty netloc), 'https://?q=1', or 'http://#fragment'. Typically produced by string templating that interpolates an empty host variable: format!("https://{}/x", host) with host == "".
Common situations: LLM-generated URLs that truncate after the scheme, config-driven URL builders with a missing hostname key, and copy-paste of scheme-only prefixes during testing.
Related errors
- IPv6 hosts are not supported in browser_open
- URL must include a valid host
- Embedding API error {status}: {text}
- Node request failed: {} {}
- Failed to open URL with default browser launchers; Brave com
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/fdfbe75e81d6288c.
Report an issue: GitHub.