aaif-goose/goose · error
Invalid base URL: {}
Error message
Invalid base URL: {} What it means
ApiClient::build_url parses the configured host string with url::Url::parse before joining any request path. If the host is not an absolute URL — no scheme, empty, or containing characters the URL parser rejects (spaces, control chars) — this error fires before any network traffic happens.
Source
Thrown at crates/goose-providers/src/api_client.rs:457
self.request(path).api_post(payload).await
}
pub async fn response_post(&self, path: &str, payload: &Value) -> Result<Response> {
self.request(path).response_post(payload).await
}
pub async fn api_get(&self, path: &str) -> Result<ApiResponse> {
self.request(path).api_get().await
}
pub async fn response_get(&self, path: &str) -> Result<Response> {
self.request(path).response_get().await
}
fn build_url(&self, path: &str) -> Result<url::Url> {
use url::Url;
let mut base_url =
Url::parse(&self.host).map_err(|e| anyhow::anyhow!("Invalid base URL: {}", e))?;
let base_path = base_url.path();
if !base_path.is_empty() && base_path != "/" && !base_path.ends_with('/') {
base_url.set_path(&format!("{}/", base_path));
}
let mut url = base_url
.join(path)
.map_err(|e| anyhow::anyhow!("Failed to construct URL: {}", e))?;
for (key, value) in &self.default_query {
url.query_pairs_mut().append_pair(key, value);
}
Ok(url)
}
}
View on GitHub (pinned to 3810898a74)
Solutions
- Prefix the scheme: 'https://api.example.com/v1' not 'api.example.com/v1'
- Print the raw value with delimiters (format!('[{}]', host)) to expose invisible whitespace, then trim it
- If the URL comes from env/config, fix it at the source (export line or YAML) rather than patching code
Example fix
# before base_url = "api.example.com/v1" # Invalid base URL: relative URL without a base # after base_url = "https://api.example.com/v1"
Defensive patterns
Strategy: validation
Validate before calling
fn valid_base_url(host: &str) -> anyhow::Result<url::Url> {
url::Url::parse(host.trim())
.map_err(|e| anyhow::anyhow!("base URL '{host}' invalid: {e} (did you forget https://?)"))
} Try / catch
// Validate before constructing the provider; on error, re-prompt for the URL // rather than surfacing the parse failure mid-session: let base = valid_base_url(&cfg.base_url)?; let client = ApiClient::new(base.to_string(), auth);
Prevention
- Always write base URLs with an explicit scheme in config files and env vars
- Trim and validate URLs at config-load time (fail fast at startup, not on first request)
- Add a config schema/lint that rejects hostnames without ://
When it happens
Trigger: Creating an ApiClient (directly or via a provider constructor) whose host/base_url is 'api.example.com/v1' without https://, is an empty string, contains a trailing copy/paste space or newline, or uses an unsupported scheme like 'example:foo'.
Common situations: Users set base URLs in provider config/env vars and forget the scheme (plain hostnames are valid in curl but not in Url::parse); env vars pick up quotes or whitespace from shell export lines; YAML config values wrap across lines producing embedded newlines.
Related errors
- External ACP backend URL is required
- External ACP backend URL must use http: or https:, got ${url
- External ACP backend URL must not include query parameters o
- External ACP backend URL must be the base URL before /acp
- Failed to construct URL: {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/37c514146a49ead7.
Report an issue: GitHub.