{"record":{"id":"9b5092d2cddc3c37","repo":"Hmbown/CodeWhale","slug":"deepseek-harness-import-requires-a-dsh-cli-grant","errorCode":null,"errorMessage":"DeepSeek Harness import requires a dsh_cli grant, not {}","messagePattern":"DeepSeek Harness import requires a dsh_cli grant, not (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/dsh_credentials.rs","lineNumber":18,"sourceCode":"//! Read-only DeepSeek Harness credential import.\n//!\n//! Official `dsh` stores API keys as a YAML mapping in\n//! `$DSH_HOME/.credentials.yaml`. Codewhale may read `DEEPSEEK_API_KEY` from\n//! that exact file only after `codewhale auth external-consent`. The file is\n//! never written, refreshed, or loaded into the process environment.\n\nuse anyhow::{Result, bail};\nuse codewhale_config::ExternalCredentialReadGrant;\n\nconst DEEPSEEK_API_KEY_REF: &str = \"DEEPSEEK_API_KEY\";\n\n/// Extract the DeepSeek API key from a granted dsh credentials document.\npub(crate) fn deepseek_api_key_from_grant(\n    grant: &ExternalCredentialReadGrant,\n) -> Result<Option<String>> {\n    if grant.source() != codewhale_config::ExternalCredentialSource::DshCli {\n        bail!(\n            \"DeepSeek Harness import requires a dsh_cli grant, not {}\",\n            grant.source().as_str()\n        );\n    }\n    let Some(text) = crate::external_credentials::read_to_string(grant)? else {\n        return Ok(None);\n    };\n    parse_dsh_deepseek_api_key(&text)\n}\n\n/// Strict subset of dsh-credentials-local: a mapping of POSIX identifiers to\n/// non-empty strings. Nested values, empty strings, and duplicate keys fail\n/// closed. Only `DEEPSEEK_API_KEY` is returned.\npub(crate) fn parse_dsh_deepseek_api_key(text: &str) -> Result<Option<String>> {\n    let mut found = None;\n    let mut seen = std::collections::BTreeSet::new();\n    for (index, raw) in text.lines().enumerate() {\n        let line = raw.trim();","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/dsh_credentials.rs#L1-L36","documentation":"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.","triggerScenarios":"Passing a grant created for a different source (any codewhale_config::ExternalCredentialSource variant other than DshCli) into deepseek_api_key_from_grant.","commonSituations":"Wiring the import to the wrong credential store after refactoring grant plumbing; a caller reusing a generic grant helper without setting the source.","solutions":["Construct the grant from the dsh credentials store so grant.source() is DshCli","Route non-dsh credential sources through their own readers instead of this function","Check grant.source().as_str() before calling if the source can vary"],"exampleFix":"// before\nlet grant = ExternalCredentialReadGrant::new(path, ExternalCredentialSource::EnvFile);\nlet key = dsh_credentials::deepseek_api_key_from_grant(&grant)?;\n\n// after\nlet grant = ExternalCredentialReadGrant::new(path, ExternalCredentialSource::DshCli);\nlet key = dsh_credentials::deepseek_api_key_from_grant(&grant)?;","handlingStrategy":"validation","validationCode":"use codewhale_config::{ExternalCredentialReadGrant, ExternalCredentialSource};\n\nfn import_deepseek(grant: &ExternalCredentialReadGrant) -> Result<Option<String>> {\n    if grant.source() != ExternalCredentialSource::DshCli {\n        return Err(anyhow::anyhow!(\n            \"refusing import: grant source is {} but this path requires dsh_cli\",\n            grant.source().as_str()\n        ));\n    }\n    dsh_credentials::deepseek_api_key_from_grant(grant)\n}","typeGuard":"fn is_dsh_grant(grant: &ExternalCredentialReadGrant) -> bool {\n    grant.source() == codewhale_config::ExternalCredentialSource::DshCli\n}","tryCatchPattern":null,"preventionTips":["Construct grants with the source fixed at creation so the type carries the provenance","Keep one import function per credential source instead of a generic dispatcher","Assert the source in tests for every import path so wiring regressions fail early"],"tags":["rust","tui","credentials","grant","security","deepseek"],"backgroundTag":"invalid-grant-type","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}