{"record":{"id":"db05df3a97dbe824","repo":"Hmbown/CodeWhale","slug":"deepseek-harness-credentials-declare-key-more","errorCode":null,"errorMessage":"DeepSeek Harness credentials declare `{key}` more than once","messagePattern":"DeepSeek Harness credentials declare `(.+?)` more than once","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/dsh_credentials.rs","lineNumber":54,"sourceCode":"        let line = raw.trim();\n        if line.is_empty() || line.starts_with('#') {\n            continue;\n        }\n        let Some((key, value)) = line.split_once(':') else {\n            bail!(\n                \"DeepSeek Harness credentials line {} is not `KEY: value`\",\n                index + 1\n            );\n        };\n        let key = key.trim();\n        if !is_posix_identifier(key) {\n            bail!(\n                \"DeepSeek Harness credentials line {} has a non-identifier key\",\n                index + 1\n            );\n        }\n        if !seen.insert(key.to_string()) {\n            bail!(\"DeepSeek Harness credentials declare `{key}` more than once\");\n        }\n        let value = unquote_yaml_string(value.trim()).map_err(|reason| {\n            anyhow::anyhow!(\n                \"DeepSeek Harness credentials line {} is invalid: {reason}\",\n                index + 1\n            )\n        })?;\n        if value.is_empty() {\n            bail!(\n                \"DeepSeek Harness credentials line {} has an empty value\",\n                index + 1\n            );\n        }\n        if key == DEEPSEEK_API_KEY_REF {\n            found = Some(value);\n        }\n    }\n    Ok(found)","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/dsh_credentials.rs#L36-L72","documentation":"Thrown by parse_dsh_deepseek_api_key when the same key appears on two different lines: the seen BTreeSet rejects the second occurrence. Duplicate mapping keys are ambiguous in YAML too, so the parser fails closed instead of picking a winner.","triggerScenarios":"A file containing DEEPSEEK_API_KEY twice — e.g. once commented-out history re-enabled, once appended at the bottom — or a copy-paste that duplicated a block.","commonSituations":"Appending a new key without noticing one already exists; merging two credential files by concatenation; editors duplicating lines.","solutions":["Delete the duplicate line so each key appears exactly once","If two values differ, decide which is current and keep only that one","Sort or dedupe the file before importing to catch repeats"],"exampleFix":"# before\nDEEPSEEK_API_KEY: sk-old\nOTHER_KEY: v\nDEEPSEEK_API_KEY: sk-new\n\n# after\nDEEPSEEK_API_KEY: sk-new\nOTHER_KEY: v","handlingStrategy":"validation","validationCode":"fn has_duplicate_keys(text: &str) -> Result<()> {\n    let mut seen = std::collections::BTreeSet::new();\n    for (index, raw) in text.lines().enumerate() {\n        let line = raw.trim();\n        if line.is_empty() || line.starts_with('#') { continue; }\n        let key = line.split_once(':').context(\"missing colon\")?.0.trim().to_string();\n        if !seen.insert(key.clone()) {\n            return Err(anyhow::anyhow!(\"line {}: duplicate key {key}\", index + 1));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never merge credential files by concatenation; merge key by key","After appending a key, search the file for an earlier occurrence","Treat duplicate-key rejection as a safety feature: resolve which value is intended, do not reorder"],"tags":["rust","tui","credentials","duplicate-key","parse-error","deepseek"],"backgroundTag":"duplicate-config-key","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}