Hmbown/CodeWhale · error · anyhow::Error

Ollama Cloud API key not found. Get a key: {}. Run 'codewhal

Error message

Ollama Cloud API key not found. Get a key: {}. Run 'codewhale auth set --provider ollama', set OLLAMA_API_KEY, or add [providers.ollama] api_key in ~/.codewhale/config.toml.

What it means

Ollama has two personalities: local self-hosted (keyless when the route is loopback/self-hosted, returning an empty key so the Authorization header is omitted) and Ollama Cloud. When the route is NOT keyless-self-hosted, this error demands a cloud key via auth set, OLLAMA_API_KEY, or [providers.ollama] api_key, with the cloud console URL interpolated (defaulting to OLLAMA_CLOUD_API_KEY_URL).

Source

Thrown at crates/tui/src/config.rs:6438

                anyhow::bail!(
                    "xAI API key not found. Get a key: https://console.x.ai/\n\
                     Run 'codewhale auth set --provider xai', set XAI_API_KEY, or add \
                     [providers.xai] api_key.\n\
                     OAuth alternative: run `codewhale auth xai-device` for \
                     Codewhale-owned storage and set [providers.xai] auth_mode = \"oauth\"."
                );
            }
            // Self-hosted deployments commonly run without auth on localhost.
            // Return an empty key and let the client omit the Authorization header.
            ApiProvider::Sglang | ApiProvider::Vllm => Ok(String::new()),
            ApiProvider::Ollama
                if provider_route_is_keyless_self_hosted(provider, &self.deepseek_base_url()) =>
            {
                Ok(String::new())
            }
            ApiProvider::Ollama => {
                let help = credential_help_for_provider_route(provider, &self.deepseek_base_url());
                anyhow::bail!(
                    "Ollama Cloud API key not found. Get a key: {}. Run 'codewhale auth set --provider ollama', set OLLAMA_API_KEY, or add [providers.ollama] api_key in ~/.codewhale/config.toml.",
                    help.credential_url
                        .unwrap_or(codewhale_config::provider::OLLAMA_CLOUD_API_KEY_URL)
                )
            }
            // Custom OpenAI-compatible endpoints (#1519): the key comes from the
            // env var named by `[providers.<name>] api_key_env`. If we reached
            // here it is unset/empty (and the endpoint is not loopback).
            ApiProvider::Custom => {
                let provider_name = self.provider.as_deref().unwrap_or("<name>");
                match self
                    .provider_config_for(provider)
                    .and_then(|entry| entry.api_key_env.as_deref())
                    .map(str::trim)
                    .filter(|name| !name.is_empty())
                {
                    Some(env_name) => anyhow::bail!(
                        "Custom provider '{provider_name}' API key not found.\n\

View on GitHub (pinned to 8880682c63)

Solutions

  1. Run codewhale auth set --provider ollama.
  2. Or export OLLAMA_API_KEY=<cloud key>.
  3. Or add api_key under [providers.ollama] in ~/.codewhale/config.toml.
  4. If you meant local Ollama, point base_url back at your loopback instance so the keyless self-hosted path applies.

Example fix

# before
provider = "ollama"
base_url = "https://remote-host:11434/v1"   # non-loopback → cloud rules

# after (option 1)
# export OLLAMA_API_KEY=...
# after (option 2, local keyless)
base_url = "http://127.0.0.1:11434/v1"
Defensive patterns

Strategy: validation

Validate before calling

fn ollama_key_present(cfg: &Config) -> bool {
    if provider_route_is_keyless_self_hosted(ApiProvider::Ollama, &cfg.deepseek_base_url()) {
        return true; // loopback self-hosted stays keyless
    }
    env_nonempty("OLLAMA_API_KEY")
        || cfg.provider_config_for(ApiProvider::Ollama)
            .and_then(|pc| pc.api_key)
            .is_some_and(|k| !k.trim().is_empty())
        || secret_store_has(ApiProvider::Ollama)
}

anyhow::ensure!(ollama_key_present(&config), "Ollama Cloud requires a key; loopback is keyless");

Type guard

fn ollama_route_needs_key(base_url: &str) -> bool {
    !base_url_uses_local_host(&base_url.to_string())
        && !provider_route_is_keyless_self_hosted(ApiProvider::Ollama, &base_url.to_string())
}

Try / catch

match config.deepseek_api_key() {
    Err(e) if e.to_string().starts_with("Ollama Cloud API key not found") => {
        // either supply a cloud key or repoint base_url at the loopback instance
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: provider = ollama with a non-loopback base_url (or default cloud route) and no OLLAMA_API_KEY, no stored key, and no [providers.ollama] api_key.

Common situations: Pointing ollama at a remote/cloud endpoint while assuming keyless local behavior; base_url typo changing localhost to a remote host; new cloud accounts.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/fd3659670ba4ced1. Report an issue: GitHub.