Hmbown/CodeWhale · error · anyhow::Error

DeepSeek Harness import requires a dsh_cli grant, not {}

Error message

DeepSeek Harness import requires a dsh_cli grant, not {}

What it means

Thrown by deepseek_api_key_from_grant when the supplied ExternalCredentialReadGrant's source is not ExternalCredentialSource::DshCli. The DeepSeek Harness import path only consumes dsh-credentials documents; grants pointing at other credential stores are rejected before any file I/O happens.

Source

Thrown at crates/tui/src/dsh_credentials.rs:18

//! Read-only DeepSeek Harness credential import.
//!
//! Official `dsh` stores API keys as a YAML mapping in
//! `$DSH_HOME/.credentials.yaml`. Codewhale may read `DEEPSEEK_API_KEY` from
//! that exact file only after `codewhale auth external-consent`. The file is
//! never written, refreshed, or loaded into the process environment.

use anyhow::{Result, bail};
use codewhale_config::ExternalCredentialReadGrant;

const DEEPSEEK_API_KEY_REF: &str = "DEEPSEEK_API_KEY";

/// Extract the DeepSeek API key from a granted dsh credentials document.
pub(crate) fn deepseek_api_key_from_grant(
    grant: &ExternalCredentialReadGrant,
) -> Result<Option<String>> {
    if grant.source() != codewhale_config::ExternalCredentialSource::DshCli {
        bail!(
            "DeepSeek Harness import requires a dsh_cli grant, not {}",
            grant.source().as_str()
        );
    }
    let Some(text) = crate::external_credentials::read_to_string(grant)? else {
        return Ok(None);
    };
    parse_dsh_deepseek_api_key(&text)
}

/// Strict subset of dsh-credentials-local: a mapping of POSIX identifiers to
/// non-empty strings. Nested values, empty strings, and duplicate keys fail
/// closed. Only `DEEPSEEK_API_KEY` is returned.
pub(crate) fn parse_dsh_deepseek_api_key(text: &str) -> Result<Option<String>> {
    let mut found = None;
    let mut seen = std::collections::BTreeSet::new();
    for (index, raw) in text.lines().enumerate() {
        let line = raw.trim();

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Construct the grant from the dsh credentials store so grant.source() is DshCli
  2. Route non-dsh credential sources through their own readers instead of this function
  3. Check grant.source().as_str() before calling if the source can vary

Example fix

// before
let grant = ExternalCredentialReadGrant::new(path, ExternalCredentialSource::EnvFile);
let key = dsh_credentials::deepseek_api_key_from_grant(&grant)?;

// after
let grant = ExternalCredentialReadGrant::new(path, ExternalCredentialSource::DshCli);
let key = dsh_credentials::deepseek_api_key_from_grant(&grant)?;
Defensive patterns

Strategy: validation

Validate before calling

use codewhale_config::{ExternalCredentialReadGrant, ExternalCredentialSource};

fn import_deepseek(grant: &ExternalCredentialReadGrant) -> Result<Option<String>> {
    if grant.source() != ExternalCredentialSource::DshCli {
        return Err(anyhow::anyhow!(
            "refusing import: grant source is {} but this path requires dsh_cli",
            grant.source().as_str()
        ));
    }
    dsh_credentials::deepseek_api_key_from_grant(grant)
}

Type guard

fn is_dsh_grant(grant: &ExternalCredentialReadGrant) -> bool {
    grant.source() == codewhale_config::ExternalCredentialSource::DshCli
}

Prevention

When it happens

Trigger: Passing a grant created for a different source (any codewhale_config::ExternalCredentialSource variant other than DshCli) into deepseek_api_key_from_grant.

Common situations: Wiring the import to the wrong credential store after refactoring grant plumbing; a caller reusing a generic grant helper without setting the source.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/9b5092d2cddc3c37. Report an issue: GitHub.