Hmbown/CodeWhale · error · anyhow::Error
API key not found. Run 'codewhale auth set --provider '…
Error message
{} API key not found.{} Run 'codewhale auth set --provider {}', set {}, or add [{}] api_key in ~/.codewhale/config.toml. What it means
Anthropic and Openmodel routes share the generic missing-key formatter missing_provider_api_key_message (crates/tui/src/config.rs:12020). The message interpolates the provider display name, an optional 'Get a key: URL.' hint from provider metadata, the provider slug for 'codewhale auth set', the env var label, and the config table name. It fires when the whole credential chain for these providers is empty and no earlier arm (custom endpoint, OAuth) matched.
Solutions
- Run 'codewhale auth set --provider anthropic' (or the Openmodel slug shown in the error)
- Export the env var named by env_vars_label (e.g. ANTHROPIC_API_KEY for the route)
- Add api_key under the table named in the error ([providers.anthropic] / [providers.openmodel]) in ~/.codewhale/config.toml
Example fix
# before [providers.anthropic] model = "claude-..." # after [providers.anthropic] model = "claude-..." api_key = "sk-ant-..."
Defensive patterns
Strategy: try-catch
Validate before calling
if matches!(provider, ApiProvider::Anthropic | ApiProvider::Openmodel) {
let present = std::env::var(provider_env_var(provider))
.is_ok_and(|v| !v.trim().is_empty())
|| route_table(provider).api_key.is_some()
|| auth_store_has(provider);
if !present {
eprintln!("missing {} key; run codewhale auth set --provider {}", provider.display_name(), provider.as_str());
}
} Try / catch
match config.deepseek_api_key() {
Err(e) if e.to_string().contains("API key not found")
&& matches!(provider, ApiProvider::Anthropic | ApiProvider::Openmodel) =>
{
// the message already lists auth set / env var / config table; prompt and retry
}
other => other?,
} Prevention
- Run 'codewhale auth set --provider <slug>' during environment provisioning, before first launch
- Use the env_vars_label from provider metadata rather than guessing ANTHROPIC_API_KEY-style names
- After 'auth logout', expect stored keys to be gone and re-provision before restarting
When it happens
Trigger: provider is ApiProvider::Anthropic or ApiProvider::Openmodel, no custom endpoint, and no api_key in config/auth store/env for the route. The dedicated match arm calls missing_provider_api_key_message(provider)? and bails with the formatted string.
Common situations: New machine without ANTHROPIC_API_KEY; the key living only in a CI secret while testing locally; switching from another provider and forgetting the Anthropic credential; logout having cleared stored keys.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- Cloud agents are not available for this account yet; cloud…
- Cloud agents are not available for this account yet; cloud…
- cloud dispatch fails closed: the cloud agent runs Codewhale…
- DeepSeek API key not found. 1. Get a key: …
- external credential access for
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/9dee9677f1c596c0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/config.rs:6710
.unwrap_or(KIMI_CODE_MEMBERSHIP_PLAN_CONSOLE_URL),
provider.env_vars_label(),
provider_config_table_name(provider)?
);
}
anyhow::bail!(
"Moonshot/Kimi API key not found. Get a key: {}. Run 'codewhale auth set --provider moonshot', \
set {}, or add [{}] api_key. \
For a Kimi Code plan key, set [providers.moonshot] base_url = \
\"https://api.kimi.com/coding/v1\" and model = \"kimi-for-coding\".",
credential_help
.credential_url
.unwrap_or("https://platform.kimi.ai/console/api-keys"),
provider.env_vars_label(),
provider_config_table_name(provider)?
);
}
ApiProvider::Anthropic | ApiProvider::Openmodel => {
anyhow::bail!("{}", missing_provider_api_key_message(provider)?)
}
ApiProvider::OpencodeZen => {
anyhow::bail!("{}", missing_provider_api_key_message(provider)?)
}
ApiProvider::OpenaiCodex => anyhow::bail!("{}", crate::oauth::missing_auth_message()),
ApiProvider::Xai => {
// Prefer OAuth guidance when auth_mode requests it or Grok CLI
// tokens already exist; otherwise show both API-key and OAuth.
if self
.provider_config_for(provider)
.is_some_and(provider_config_uses_xai_oauth)
|| crate::xai_oauth::credentials_present(self)
{
anyhow::bail!("{}", crate::xai_oauth::missing_auth_message());
}
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 \View on GitHub (pinned to 0c42157ee5)