{"record":{"id":"c20a24144414cc0e","repo":"affaan-m/ECC","slug":"candidate-id-collision-with-different-immutable-co","errorCode":null,"errorMessage":"candidate id collision with different immutable content","messagePattern":"candidate id collision with different immutable content","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/session/store.rs","lineNumber":5394,"sourceCode":"                anyhow::bail!(\"legacy candidate id collision with different immutable content\");\n            }\n            let tx = self.conn.unchecked_transaction()?;\n            Self::register_harness_alias(&tx, &candidate.id, &legacy_id)?;\n            tx.commit()?;\n            return Ok(());\n        }\n        self.conn.execute(\n            \"INSERT INTO harness_candidates (id, canonical_config_json, trace_refs_json, evidence_refs_json, created_at)\n             VALUES (?1, ?2, ?3, ?4, ?5) ON CONFLICT(id) DO NOTHING\",\n            rusqlite::params![candidate.id, candidate.canonical_config, trace_json, evidence_json, chrono::Utc::now().to_rfc3339()],\n        )?;\n        let stored: (String, String, String) = self.conn.query_row(\n            \"SELECT canonical_config_json, trace_refs_json, evidence_refs_json FROM harness_candidates WHERE id = ?1\",\n            [&candidate.id],\n            |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),\n        )?;\n        if stored != expected {\n            anyhow::bail!(\"candidate id collision with different immutable content\");\n        }\n        Ok(())\n    }\n\n    pub fn activate_initial_harness(&self, candidate_id: &str, evidence_ref: &str) -> Result<()> {\n        if candidate_id.len() != 64 || evidence_ref.trim().is_empty() || evidence_ref.len() > 4096 {\n            anyhow::bail!(\n                \"valid candidate id and bounded activation evidence reference are required\"\n            );\n        }\n        let tx = self.conn.unchecked_transaction()?;\n        let stored_candidate_id = Self::resolve_harness_candidate_id(&tx, candidate_id)?;\n        if tx\n            .query_row(\n                \"SELECT candidate_id FROM active_harness_config WHERE slot = 'default'\",\n                [],\n                |row| row.get::<_, String>(0),\n            )","sourceCodeStart":5376,"sourceCodeEnd":5412,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/session/store.rs#L5376-L5412","documentation":"A v2 candidate id is content-addressed. After INSERT ... ON CONFLICT(id) DO NOTHING the store reads back the persisted (canonical_config_json, trace_refs_json, evidence_refs_json) and compares to the expected triple. A mismatch means two different contents resolved to the same id — a hash collision is not expected, so this indicates corruption, a serialization bug, or a hash-function drift.","triggerScenarios":"register_candidate where candidate.id already exists in harness_candidates with different canonical_config_json/trace_refs_json/evidence_refs_json than the expected triple being inserted.","commonSituations":"JSON key ordering or whitespace changed between when candidate.id was hashed and when it is re-serialized for storage; two code paths compute candidate.id with different rules; a manual DB edit changed one column.","solutions":["Guarantee candidate.id is computed from exactly the canonical_config/trace_refs/evidence_refs being stored (recompute and compare before insert).","Use a canonical, deterministic JSON serializer (sorted keys, no extra whitespace) for all three fields before hashing.","If the stored row is the wrong one, delete it and re-insert the correct triple."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Recompute the id from exactly what you are about to store, and confirm it is stable.\nlet expected_id = candidate.id_for_v2()?;\nif expected_id != candidate.id {\n    return Err(anyhow::anyhow!(\"candidate.id does not match recomputed content id\"));\n}\n// Build the expected stored triple with the SAME canonical serializer the store uses.\nlet expected = (candidate.canonical_config.clone(), trace_json.clone(), evidence_json.clone());\n// (the store compares SELECT ... WHERE id = candidate.id against `expected`)","typeGuard":"fn candidate_id_is_stable(candidate: &CandidateSpec) -> bool {\n    candidate.id_for_v2().ok().as_deref() == Some(candidate.id.as_str())\n}","tryCatchPattern":"match store.register_candidate(&candidate) {\n    Ok(()) => { /* ok */ }\n    Err(e) if e.to_string().contains(\"candidate id collision with different immutable content\") => {\n        // two different contents hashed to the same id — investigate serialization/hash,\n        // do not retry with the same inputs.\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always compute candidate.id from the same canonical_config/trace_refs/evidence_refs being persisted, using a deterministic serializer (sorted keys, fixed whitespace).","Unit-test that re-serializing a stored candidate reproduces the identical id.","Never let two code paths compute the id with different rules."],"tags":["rust","sqlite","harness","content-addressing","integrity"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}