{"record":{"id":"3c20b814080ad804","repo":"affaan-m/ECC","slug":"invalid-canonical-health-evidence-snapshot","errorCode":null,"errorMessage":"invalid canonical health evidence snapshot","messagePattern":"invalid canonical health evidence snapshot","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/harness_eval.rs","lineNumber":348,"sourceCode":"    pub fn canonical_json(&self) -> Result<String> {\n        self.verify()?;\n        Ok(serde_json::to_string(self)?)\n    }\n\n    pub fn digest(&self) -> Result<String> {\n        Ok(sha256_hex(self.canonical_json()?.as_bytes()))\n    }\n\n    pub fn verify(&self) -> Result<()> {\n        if self.schema_version != 1\n            || self.evaluator != \"recorded-v1\"\n            || self.candidate_id.len() != 64\n            || !self\n                .candidate_id\n                .bytes()\n                .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n        {\n            bail!(\"invalid canonical health evidence snapshot\");\n        }\n        Ok(())\n    }\n}\n\npub struct RecordedEvaluator {\n    name: String,\n    scores: BTreeMap<(String, u64), f64>,\n    health_ok: bool,\n    health_candidate: Option<String>,\n    calls: Vec<(String, u64)>,\n}\n\nimpl RecordedEvaluator {\n    #[cfg(test)]\n    pub fn new(scores: BTreeMap<(String, u64), f64>, health_ok: bool) -> Self {\n        Self {\n            name: \"recorded-v1\".into(),","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/harness_eval.rs#L330-L366","documentation":"RecordedEvidence::verify checks that the evidence snapshot is canonical: candidate_id must be exactly 64 lowercase hex characters (plus other structural checks). This error means the health evidence snapshot has a malformed candidate id or otherwise fails canonical-form validation, so it cannot be trusted as a canonical health record.","triggerScenarios":"Loading or deserializing a RecordedEvidence whose candidate_id is shorter/longer than 64 chars, contains non-hex characters (uppercase A-F, 'g'-'z', punctuation), or is empty; then calling verify (directly or via canonical_json / evaluate_promote_and_health_check).","commonSituations":"Truncating or reformatting the candidate id (e.g. uppercasing through a shell pipeline); copying an id from logs that wrapped lines; constructing evidence by hand in a test with a placeholder id like \"test-candidate\"; exporting data through a system that altered the id's case.","solutions":["Use the id returned by HarnessCandidate (e.g. id_for_v2()/id_for_v1()) verbatim; never retype or transform it.","Lowercase the id and re-check it is 64 hex chars before persisting evidence: id.to_lowercase() with a length/charset check.","Regenerate the evidence snapshot from the recorded evaluator flow so the id is derived from the candidate, not supplied manually.","Fix the export/import step that altered the id (e.g. case-folding, truncation, or line-wrapping)."],"exampleFix":"// before\nlet evidence = RecordedEvidence { candidate_id: params.candidate_id.to_uppercase(), .. };\n\n// after\nlet candidate_id = params.candidate_id.trim().to_lowercase();\nassert_eq!(candidate_id.len(), 64);\nassert!(candidate_id.bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()));\nlet evidence = RecordedEvidence { candidate_id, .. };","handlingStrategy":"type-guard","validationCode":"fn is_canonical_candidate_id(id: &str) -> bool {\n    id.len() == 64\n        && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))\n}","typeGuard":"fn narrow_candidate_id(raw: &str) -> Option<CandidateId> {\n    let s = raw.trim().to_lowercase();\n    (s.len() == 64 && s.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)))\n        .then(|| CandidateId(s))\n}","tryCatchPattern":"match evidence.verify() {\n    Ok(()) => proceed(),\n    Err(e) if e.to_string().contains(\"invalid canonical health evidence\") => bail!(\"corrupt candidate id in evidence; regenerate snapshot\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Copy candidate ids programmatically from API return values, never by retyping.","Lowercase hex ids before persisting; reject ids that fail a 64-hex-char check at ingestion.","Avoid shell pipelines (tr, cut, word wrap) that transform ids in logs or exports."],"tags":["validation","identifier","hex"],"backgroundTag":"invalid-identifier-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}