zeroclaw-labs/zeroclaw · error · anyhow::Error
URL must include a valid host
Error message
URL must include a valid host
What it means
Thrown by extract_host in the http_request tool after the URL parsed and passed the scheme and userinfo checks, but its host reduced to an empty string once trailing root-label dots were stripped and the value lowercased. In other words the host consisted solely of one or more '.' characters (for example 'https://.'). A dot-only host cannot be resolved or SSRF-validated, so the request is rejected before any network I/O happens.
Source
Thrown at crates/zeroclaw-tools/src/http_request.rs:746
anyhow::bail!("URL userinfo is not allowed");
}
let host = parsed
.host_str()
.ok_or_else(|| anyhow::Error::msg("URL must include a host"))?;
let trimmed = host.trim();
let host_no_brackets = match (trimmed.starts_with('['), trimmed.ends_with(']')) {
(true, true) => &trimmed[1..trimmed.len() - 1],
(false, false) => trimmed,
_ => {
anyhow::bail!("URL host has unmatched IPv6 brackets");
}
};
let host = host_no_brackets.trim_end_matches('.').to_lowercase();
if host.is_empty() {
anyhow::bail!("URL must include a valid host");
}
Ok(host)
}
fn extract_port(url: &str) -> anyhow::Result<u16> {
let parsed = reqwest::Url::parse(url)
.map_err(|e| anyhow::Error::msg(format!("Invalid URL format: {e}")))?;
parsed
.port_or_known_default()
.ok_or_else(|| anyhow::Error::msg("URL must include a valid port"))
}
async fn resolve_host_for_request(host: String, port: u16) -> anyhow::Result<Vec<SocketAddr>> {
let addrs = tokio::net::lookup_host((host.as_str(), port))
.await
.map_err(|e| anyhow::Error::msg(format!("Failed to resolve host '{host}': {e}")))?View on GitHub (pinned to 88bb9c8533)
Solutions
- Replace the dot-only host with a real hostname, e.g. 'https://api.example.com/path'.
- If the URL is built dynamically, print it before invoking the tool and fix the interpolation that left a placeholder in the host position.
- Pre-validate hosts (non-empty after trimming trailing dots) before passing URLs to the tool.
Example fix
// before
let url = format!("https://{}", host_placeholder); // host_placeholder = "."
tool_http_request(url).await?; // -> URL must include a valid host
// after
let url = format!("https://api.example.com{}", path);
tool_http_request(url).await?; Defensive patterns
Strategy: validation
Validate before calling
fn has_valid_http_host(url: &str) -> bool {
let Ok(parsed) = reqwest::Url::parse(url) else { return false };
match parsed.host_str() {
Some(h) => !h.trim().trim_end_matches('.').is_empty(),
None => false,
}
} Type guard
fn is_http_request_url(url: &str) -> bool {
(url.starts_with("http://") || url.starts_with("https://")) && has_valid_http_host(url)
} Try / catch
if let Err(e) = run_http_request(url).await {
if e.to_string().contains("URL must include a valid host") {
// treat as caller input error: surface a fixable message, do not retry
}
} Prevention
- Never build URLs by string interpolation with unfilled placeholders; assert the host segment is non-empty before invoking the tool.
- Reject dot-only or empty hosts in your own URL validation layer before passing URLs to the agent.
When it happens
Trigger: Calling the http_request tool with a URL whose host part is only dots: 'http://.', 'https://..', 'https://...'. Any input where reqwest::Url::parse succeeds and host_str() returns a non-empty string made entirely of '.' characters hits this bail at http_request.rs:746. It cannot be reached with a truly empty host (that is the earlier 'URL must include a host' error at line 733) or with unmatched IPv6 brackets (line 740).
Common situations: Placeholder URLs pasted from templates or documentation ('http://...'), typos where dots were typed instead of a hostname, dynamic construction like format!("https://{}", placeholder) where the placeholder was never filled in, and test fixtures that used dot-only hosts.
Related errors
- providers.models.{profile_name}.uri must use http/https
- Generated image URL must be a non-empty URL without whitespa
- Generated image URL must use HTTPS
- Generated image URL userinfo is not allowed
- Generated image URL host must not end with a dot
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b3900353943296b0.
Report an issue: GitHub.