Hmbown/CodeWhale · error

DeepSeek stores auth at the root config level

Error message

DeepSeek stores auth at the root config level

What it means

`provider_config_key` maps an `ApiProvider` to its per-provider config key, but DeepSeek (and DeepSeek CN) intentionally have no per-provider slot: their auth is stored at the root config level. The function bails rather than returning a key that would create a parallel storage location.

Solutions

  1. Store/read DeepSeek auth at the root config level, not a provider slot
  2. Use the DeepSeek-specific root-level credential path (there is a `clear_deepseek_provider_slot` parameter in the save routine for exactly this)
  3. For `codewhale auth clear --provider deepseek`, the root-level removal path handles it automatically
Defensive patterns

Strategy: try-catch

Validate before calling

if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN) {
    eprintln!("use root-level auth config for DeepSeek");
} else { let key = provider_config_key(provider)?; }

Type guard

fn has_provider_config_key(p: ApiProvider) -> bool {
    !matches!(p, ApiProvider::Deepseek | ApiProvider::DeepseekCN)
}

Try / catch

match provider_config_key(provider) {
    Err(e) if e.to_string().contains("root config level") => use_root_level_auth(provider),
    other => other?,
}

Prevention

When it happens

Trigger: Calling `provider_config_key(ApiProvider::Deepseek)` or `provider_config_key(ApiProvider::DeepseekCN)` — e.g. during credential save/delete flows that assume every provider has its own config slot.

Common situations: Generic code that iterates providers and calls `provider_config_key` for each; user scripts or TUI flows saving/deleting DeepSeek credentials through the per-provider path.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

            &["providers", key_inside, "external_credentials"],
        )?;
        Ok(())
    })
    .with_context(|| {
        format!(
            "Failed to write config to {}",
            codewhale_config::quote_os_path(&config_path)
        )
    })?;
    live_config
        .provider_config_for_mut(provider)
        .external_credentials = None;
    Ok(config_path)
}

pub(crate) fn provider_config_key(provider: ApiProvider) -> Result<&'static str> {
    if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN) {
        anyhow::bail!("DeepSeek stores auth at the root config level");
    }
    provider
        .metadata()
        .map(|metadata| metadata.provider_config_key())
        .context("provider config key")
}

fn provider_config_table_name(provider: ApiProvider) -> Result<String> {
    Ok(format!("providers.{}", provider_config_key(provider)?))
}

fn provider_env_api_key(provider: ApiProvider) -> Option<String> {
    if provider == ApiProvider::Huggingface {
        return std::env::var("HUGGINGFACE_API_KEY")
            .ok()
            .filter(|value| !value.trim().is_empty())
            .or_else(|| {
                std::env::var("HF_TOKEN")

View on GitHub (pinned to 73e0f67d83)