Hmbown/CodeWhale · error
Custom provider ' ' API key not found: the route binds…
Error message
Custom provider '{route_name}' API key not found: the route binds api_key_env = "{env_name}" but that environment variable is not set. Set {env_name} to your key, or remove api_key_env from [providers.{route_name}] to run the endpoint without credentials. What it means
Thrown during route resolution for a custom provider whose configuration binds `api_key_env` to an environment variable that is unset (or set to whitespace only). The message names the route, the env var, and both remedies: export the variable, or drop `api_key_env` to run the endpoint without credentials.
Solutions
- Set the variable: `export <env_name>=<your-key>` in the shell that launches the app
- If the endpoint needs no auth, remove the `api_key_env` line from `[providers.<route_name>]` in your config
- Verify the spelling of `api_key_env` matches the exported variable exactly (`echo "${<env_name>}"` to confirm it is non-empty)
Example fix
# before (config) [providers.myroute] api_key_env = "MYROUTE_API_KEY" # after — in the launching shell export MYROUTE_API_KEY="sk-..."
Defensive patterns
Strategy: validation
Validate before calling
let env_name = "MYROUTE_API_KEY";
if std::env::var(env_name).map_or(true, |v| v.trim().is_empty()) {
eprintln!("{env_name} is not set; the custom provider route will fail to resolve");
} Prevention
- Export API keys in the same shell/scope that launches the app (or use an env file the launcher sources)
- Grep the config for `api_key_env` at startup in scripts and assert each named variable is non-empty
- Avoid whitespace-only values; quote the key when exporting
- Remove `api_key_env` for endpoints that genuinely need no credentials
When it happens
Trigger: Starting a session or switching routes to a custom provider `[providers.<name>]` that declares `api_key_env = "..."` while `std::env::var` on that name fails or returns an empty/whitespace value.
Common situations: Exporting the key in one shell but launching the app from another; missing the variable in a service/tmux environment; a typo in the `api_key_env` name versus the actual exported variable; CI environments where secrets are not injected.
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
- Antigravity is a retired, non-runnable legacy provider…
- Antigravity is a retired, non-runnable legacy provider…
- api_key cannot be empty string
- bearer credentials are not an API key
- Cannot resume agent on its saved provider
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/180ab91c84a75935.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/config.rs:7226
// `[providers.<name>] api_key_env = "..."`. This remains safe for a
// custom endpoint because the binding belongs to that route; ambient
// provider variables below do not.
//
// For a custom provider, a binding that names an unset (or empty)
// variable is a broken credential contract, not a keyless route: fail
// loudly with the route-scoped fix instead of silently degrading to
// the self-hosted loopback keyless fallback below (#5104). Without
// this, an `api_key_env` route on a loopback host dispatched
// unauthenticated while the operator believed credentials were wired,
// and the composer-side preflight recovery never saw an error.
if provider == ApiProvider::Custom
&& let Some(env_name) = bound_provider_api_key_env_name(self, provider)
{
return match std::env::var(&env_name) {
Ok(value) if !value.trim().is_empty() => Ok(value),
_ => {
let route_name = self.provider.as_deref().unwrap_or("<name>");
Err(anyhow::anyhow!(
"Custom provider '{route_name}' API key not found: the route binds \
api_key_env = \"{env_name}\" but that environment variable is not set. \
Set {env_name} to your key, or remove api_key_env from \
[providers.{route_name}] to run the endpoint without credentials."
))
}
};
}
if let Some(value) = provider_config_env_api_key(self, provider) {
return Ok(value);
}
// 2. The dispatcher resolves this same provider slot before launching
// the TUI. Standalone `codewhale-tui` launches must see the identical
// durable credential. Auto-detection is file-backed and prompt-free by
// default; the OS keyring is queried only when the user explicitly
// selects the system backend.
if !self.should_skip_secret_store_for_provider(provider)View on GitHub (pinned to 73e0f67d83)