Hmbown/CodeWhale · error

api_key cannot be empty string

Error message

api_key cannot be empty string

What it means

An `api_key` present in config must not be blank. Empty or whitespace-only keys would be sent as credentials and fail downstream auth confusingly, so validation rejects them up front. Thrown at config.rs:5110.

Solutions

  1. Set api_key to the real key, or remove the field entirely to use env-based credentials.
  2. Verify the env var feeding the key is non-empty (e.g. GEMINI_API_KEY).
  3. Delete the blank key line rather than leaving `api_key = ""`.

Example fix

// before
api_key = ""
// after
# api_key removed; key read from environment
Defensive patterns

Strategy: validation

Validate before calling

if let Some(k) = &config.api_key {
    if k.trim().is_empty() { return Err("api_key must be non-empty or omitted"); }
}

Prevention

When it happens

Trigger: Config has `api_key = ""` or api_key containing only whitespace (trim().is_empty()); a skipped/failed env-var substitution leaving the literal empty string.

Common situations: Exporting an empty env var and interpolating it into config; clearing a key by blanking it instead of removing the field; template configs with empty placeholders committed as-is.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

                "Invalid provider '{provider}': expected {}.",
                ApiProvider::names_hint()
            );
        }
        let active_provider = self.api_provider();
        match validate_kimi_code_api_model_id(
            active_provider,
            &self.active_route_base_url(),
            &self.default_model(),
        ) {
            Err(error) if error == KIMI_CODE_CLAUDE_ALIAS_GUIDANCE => {
                return Err(SafeConfigDiagnostic::KimiCodeClaudeAlias.into());
            }
            result => result.map_err(anyhow::Error::msg)?,
        }
        if let Some(ref key) = self.api_key
            && key.trim().is_empty()
        {
            anyhow::bail!("api_key cannot be empty string");
        }
        if let Some(features) = &self.features {
            for key in features.entries.keys() {
                if !is_known_feature_key(key) {
                    anyhow::bail!("Unknown feature flag: {key}");
                }
            }
        }
        // Validate the model against the *active provider's* name space, not
        // against DeepSeek's. `canonical_model_id_for_provider` is the
        // equal-treatment resolver: it applies each family's own canonical map
        // (GLM via Z.ai, Kimi, MiniMax, …) and passes unknown ids through, so
        // it rejects only what the provider genuinely cannot serve. Validating
        // with the DeepSeek-only `normalize_model_name` bricked every config
        // whose provider owns a non-DeepSeek family — including ones our own
        // setup wizard writes (`provider = "zai"`, `GLM-5.2`). (#4829)
        // Provider-scoped choices own the active route. A retained root
        // fallback can belong to a different provider after a saved switch.

View on GitHub (pinned to 73e0f67d83)