aaif-goose/goose · error · anyhow::Error

Invalid base URL '{}': {}

Error message

Invalid base URL '{}': {}

What it means

Building the Ollama client, goose normalizes base_url: if it doesn't start with http:// or https:// it prepends http://, then parses with Url::parse. This error means parsing still failed — most commonly because base_url is empty ('http://' alone yields an empty-host parse error), or contains spaces, an invalid port (e.g. ':1143x'), or other characters the URL grammar rejects. Unlike the generic ApiClient error, this message echoes the original configured value.

Source

Thrown at crates/goose-providers/src/ollama.rs:306

        return Err(anyhow::anyhow!(
            "Provider '{}' has dynamic_models: false but no static models listed; \
             at least one entry in `models` is required.",
            config.name
        ));
    }

    let timeout = Duration::from_secs(config.timeout_seconds.unwrap_or(OLLAMA_TIMEOUT));

    let base_has_scheme =
        config.base_url.starts_with("http://") || config.base_url.starts_with("https://");
    let base = if base_has_scheme {
        config.base_url.clone()
    } else {
        format!("http://{}", config.base_url)
    };

    let mut base_url = Url::parse(&base)
        .map_err(|e| anyhow::anyhow!("Invalid base URL '{}': {}", config.base_url, e))?;

    let is_localhost = matches!(base_url.host_str(), Some("localhost" | "127.0.0.1" | "::1"));

    if base_url.port().is_none() && !base_has_scheme && is_localhost {
        base_url
            .set_port(Some(OLLAMA_DEFAULT_PORT))
            .map_err(|_| anyhow::anyhow!("Failed to set default port"))?;
    }

    let api_key = if config.api_key_env.is_empty() {
        None
    } else {
        match key_resolver.resolve_key(config.api_key_env.as_str()) {
            Ok(key) => Some(key),
            Err(err) => {
                if config.requires_auth {
                    anyhow::bail!("missing required key {}: {}", config.api_key_env, err);
                }

View on GitHub (pinned to 3810898a74)

Solutions

  1. Set a concrete URL: "base_url": "http://localhost:11434" (scheme optional for localhost, port auto-defaulted there) — but never empty
  2. Check the echoed value in the message for spaces, smart quotes, or missing pieces, and quote the value in YAML/JSON
  3. If pointing at a remote server, include scheme and port: "https://ollama.internal.example.com:11434"

Example fix

// before
{ "name": "my-ollama", "engine": "ollama", "base_url": "" }

// after
{ "name": "my-ollama", "engine": "ollama", "base_url": "http://localhost:11434" }
Defensive patterns

Strategy: validation

Validate before calling

fn ollama_base_url_ok(raw: &str) -> anyhow::Result<url::Url> {
    let with_scheme = if raw.starts_with("http://") || raw.starts_with("https://") {
        raw.to_string()
    } else {
        format!("http://{}", raw)
    };
    url::Url::parse(&with_scheme).map_err(|e| anyhow::anyhow!("base_url '{raw}': {e}"))
}

Try / catch

// Mirror goose's normalization at config-load time so users get the error at
// startup with the offending value echoed:
let base = ollama_base_url_ok(cfg.base_url.trim())?;

Prevention

When it happens

Trigger: A declarative Ollama config with "base_url": "" (the classic case: field omitted/emptied by a template), 'base_url": "my host:11434" (space), '"ollama@server"', or a malformed port like 'localhost:114_34'. Note localhost URLs without a port automatically get OLLAMA_DEFAULT_PORT (11434) applied, so that part needs no fixing.

Common situations: Config templating leaves an empty placeholder; users assume 'localhost' is enough without scheme and mangle it further; values pasted from docs pick up smart quotes or trailing whitespace; YAML unquoted strings containing ':#' sequences.

Related errors


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