Hmbown/CodeWhale · error

Kimi CLI credential import is unsupported. Codewhale does…

Error message

Kimi CLI credential import is unsupported. Codewhale does not impersonate or reuse Kimi OAuth clients; configure an API key from {} instead.

What it means

Codewhale refuses to route requests through Kimi CLI imported OAuth tokens. The library deliberately does not impersonate or reuse the Kimi CLI's OAuth client, so if the active provider route is configured to use an imported Kimi token, configuration resolution fails fast before any request is sent, pointing the user at the provider's API-key console instead.

Solutions

  1. Remove the Kimi CLI credential import / imported-token auth_mode from the provider route config.
  2. Create a Kimi (Moonshot) API key at the console URL shown in the error and set [providers.<route>] api_key or api_key_env.
  3. Run 'codewhale auth set --provider moonshot' to store the key.
  4. For Kimi Code plans, set [providers.moonshot] base_url = "https://api.kimi.com/coding/v1" and model = "kimi-for-coding" with a plan API key.

Example fix

// before
[providers.moonshot]
auth_mode = "kimi-cli-import"

// after
[providers.moonshot]
api_key = "sk-..."
Defensive patterns

Strategy: validation

Validate before calling

fn uses_kimi_import(cfg: &toml::Value) -> bool {
    cfg.get("providers").and_then(|p| p.get("moonshot"))
        .and_then(|m| m.get("auth_mode"))
        .and_then(|a| a.as_str())
        .map(|s| s.contains("kimi-cli-import"))
        .unwrap_or(false)
}
if uses_kimi_import(load_config()) {
    eprintln!("replace the kimi CLI import with api_key before starting");
}

Type guard

fn has_static_key(cfg: &ProviderCfg) -> bool {
    cfg.api_key.as_deref().map(|k| !k.trim().is_empty()).unwrap_or(false)
        || cfg.api_key_env.as_deref().map(|e| !e.trim().is_empty()).unwrap_or(false)
}

Prevention

When it happens

Trigger: The active provider route's provider config satisfies provider_config_uses_kimi_imported_token (e.g. [providers.moonshot] configured to import the Kimi CLI token) and any API call resolves credentials through Config::active_route_api_key.

Common situations: A user who previously authenticated with Kimi CLI copies that setup into their codewhale config expecting SSO to carry over; CI or dotfiles migrate a Kimi CLI credential import left over from an older codewhale version that tolerated it.

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


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

Appendix: source

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

        }
        if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN)
            && self.config_credentials_are_bound_to_provider_endpoint(provider)
            && let Some(configured) = self.api_key.as_ref()
            && classify_config_api_key_value(configured) == ConfigApiKeyValueKind::Literal
        {
            warn_on_config_api_key_shadowing(self, provider, "the root api_key");
            return Ok(configured.clone());
        }

        if provider == ApiProvider::Moonshot
            && !custom_endpoint
            && self
                .provider_config_for(provider)
                .is_some_and(provider_config_uses_kimi_imported_token)
        {
            let credential_help =
                credential_help_for_provider_route(provider, &self.active_route_base_url());
            anyhow::bail!(
                "Kimi CLI credential import is unsupported. Codewhale does not impersonate or reuse Kimi OAuth clients; configure an API key from {} instead.",
                credential_help
                    .credential_url
                    .unwrap_or("the selected provider's API-key console")
            );
        }

        // xAI OAuth prefers Codewhale-owned device-login storage. An existing
        // Grok CLI file is considered only with provider/path-scoped read-only
        // consent. Activated by [providers.xai] auth_mode = "oauth".
        if provider == ApiProvider::Xai
            && !custom_endpoint
            && self
                .provider_config_for(provider)
                .is_some_and(provider_config_uses_xai_oauth)
            && crate::oauth::credentials_present(crate::oauth::OAuthProvider::Xai, self)
        {
            return crate::oauth::get_xai_access_token(self);

View on GitHub (pinned to 73e0f67d83)