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

  1. Prefix the scheme: 'https://api.example.com/v1' not 'api.example.com/v1'
  2. Print the raw value with delimiters (format!('[{}]', host)) to expose invisible whitespace, then trim it
  3. 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

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


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/37c514146a49ead7. Report an issue: GitHub.