Hmbown/CodeWhale · error · anyhow::Error
Refusing invalid base URL
Error message
Refusing invalid base URL '{display_base_url}' What it means
validate_base_url_security parses the configured base URL with reqwest::Url::parse and refuses to build a client when the string is not a valid absolute URL. The URL is redacted for display (credentials stripped) before appearing in the message, so the error never leaks API keys. It is thrown at client construction so a malformed endpoint fails fast instead of producing confusing connection errors later.
Solutions
- Add an explicit scheme to the base URL in config.toml or the environment, e.g. https://api.openai.com/v1
- Trim whitespace and re-check the raw string around the configured value for typos or embedded characters
- If the value comes from an env var, echo it (masked) to confirm it resolves to a full URL and is non-empty
- If the URL is intentionally non-HTTP, confirm it matches what the provider expects (https or http to a loopback address)
Example fix
// before (config.toml) base_url = "api.openai.com/v1" // after base_url = "https://api.openai.com/v1"
Defensive patterns
Strategy: validation
Validate before calling
fn base_url_is_valid(base_url: &str) -> bool {
matches!(reqwest::Url::parse(base_url), Ok(u) if u.has_host() && matches!(u.scheme(), "https" | "http"))
}
if !base_url_is_valid(&cfg.base_url) {
eprintln!("base_url must be an absolute http(s) URL, got: {:?}", cfg.base_url);
}
Prevention
- Always include the scheme (https://) in configured base URLs
- Validate base_url at config-load time, before constructing any client
- Keep credentials out of the URL; use a separate api_key field so redaction is never needed
- Trim environment-substituted values to catch whitespace and empty results
When it happens
Trigger: Calling client construction/validate_base_url_security with a base_url string that reqwest::Url::parse rejects: missing scheme (e.g. "api.openai.com/v1" instead of "https://api.openai.com/v1"), whitespace, invalid characters, or a bare host with no scheme.
Common situations: Typing a base_url in config.toml without the https:// prefix; copying a URL with a trailing space or stray character; environment substitution producing an empty or partial URL; hostnames pasted without scheme when migrating provider configs.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- custom provider base URL is invalid
- outbound origin is empty or oversized
- agent profile provider cannot be empty
- agent profile provider must be a simple provider id
- api_key cannot be empty string
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/6cdee261f6f5f9c5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/client.rs:1079
{
return Err(CatalogRefreshError::InvalidResponse);
}
let mut stream = response.bytes_stream();
let mut body = Vec::new();
while let Some(chunk) = stream.next().await {
let chunk = chunk.map_err(|_| CatalogRefreshError::Network)?;
if body.len().saturating_add(chunk.len()) > max_bytes {
return Err(CatalogRefreshError::InvalidResponse);
}
body.extend_from_slice(&chunk);
}
String::from_utf8(body).map_err(|_| CatalogRefreshError::InvalidResponse)
}
fn validate_base_url_security(base_url: &str, provider_allows_insecure_http: bool) -> Result<()> {
let display_base_url = redact_url_for_display(base_url);
let parsed = reqwest::Url::parse(base_url)
.map_err(|_| anyhow::anyhow!("Refusing invalid base URL '{display_base_url}'"))?;
let loopback = parsed.host_str().is_some_and(|host| {
host.eq_ignore_ascii_case("localhost")
|| host
.trim_matches(['[', ']'])
.parse::<std::net::IpAddr>()
.is_ok_and(|address| address.is_loopback())
});
if parsed.scheme() == "https" || (parsed.scheme() == "http" && loopback) {
return Ok(());
}
if parsed.scheme() == "http" && provider_allows_insecure_http {
logging::warn(
"Using insecure HTTP base URL because this provider sets allow_insecure_http = true in config.toml",
);
return Ok(());
}
View on GitHub (pinned to 73e0f67d83)