Hmbown/CodeWhale · error

Custom provider ' ' has no auth configured. Add api_key_env…

Error message

Custom provider '{provider_name}' has no auth configured.
Add api_key_env = "YOUR_ENV_VAR" (or api_key) to [providers.{provider_name}] in ~/.codewhale/config.toml.

What it means

Thrown for a custom provider that has no auth configuration at all: no usable api_key_env name and no inline api_key. It is the `None` arm of the custom-provider key lookup and instructs the user to declare one of the two supported auth fields in the provider's config table. Without it, requests would go out with no credential.

Solutions

  1. Add `api_key_env = "YOUR_ENV_VAR"` to [providers.<name>] in ~/.codewhale/config.toml and export that variable
  2. Or add `api_key = "..."` directly to the [providers.<name>] table
  3. Check for typos: the fields are exactly `api_key` and `api_key_env`

Example fix

// before
[providers.acme]
base_url = "https://api.acme.dev/v1"
// after
[providers.acme]
base_url = "https://api.acme.dev/v1"
api_key_env = "ACME_API_KEY"
Defensive patterns

Strategy: validation

Validate before calling

// check the custom provider table declares auth before use
// config.toml must contain, under [providers.<name>]: api_key or api_key_env
let cfg = std::fs::read_to_string("~/.codewhale/config.toml")?;
let has_auth = cfg.contains("api_key") || cfg.contains("api_key_env");
if !has_auth { eprintln!("custom provider has no auth configured"); }

Try / catch

match result {
    Err(e) if e.to_string().contains("has no auth configured") => add_api_key_field_to_provider_table(),
    other => other?,
}

Prevention

When it happens

Trigger: A [providers.<name>] entry (or the provider being referenced) defines a base URL but neither api_key nor api_key_env, and the provider is used for a request.

Common situations: Hand-written custom provider table missing the auth fields; copied config from a provider that used a different auth mechanism; typo'd key names (e.g. apikey, api-key).

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/806cd99ed92454a9. Report an issue: GitHub.

Appendix: source

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

                )
            }
            // 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\
                         Set the environment variable {env_name} to your key, \
                         or add api_key to [providers.{provider_name}]."
                    ),
                    None => anyhow::bail!(
                        "Custom provider '{provider_name}' has no auth configured.\n\
                         Add api_key_env = \"YOUR_ENV_VAR\" (or api_key) to \
                         [providers.{provider_name}] in ~/.codewhale/config.toml."
                    ),
                }
            }
            _ => anyhow::bail!("{}", missing_provider_api_key_message(provider)?),
        }
    }

    /// Resolve the skills directory path.
    #[must_use]
    pub fn skills_dir(&self) -> PathBuf {
        self.skills_dir
            .as_deref()
            .map(expand_path)
            .or_else(default_skills_dir)
            .unwrap_or_else(|| PathBuf::from("./skills"))

View on GitHub (pinned to 73e0f67d83)