{"record":{"id":"f08870255df7c3c5","repo":"affaan-m/ECC","slug":"candidate-alias-integrity-verification-failed","errorCode":null,"errorMessage":"candidate alias integrity verification failed","messagePattern":"candidate alias integrity verification failed","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"ecc2/src/session/store.rs","lineNumber":985,"sourceCode":"            let target = CandidateSpec {\n                id: target_id.clone(),\n                canonical_config,\n                trace_refs: serde_json::from_str(&trace_json)?,\n                evidence_refs: serde_json::from_str(&evidence_json)?,\n            };\n            if version != 2\n                || target_id != target.legacy_id()\n                || alias_id != target.id_for_v2()?\n                || tx\n                    .query_row(\n                        \"SELECT 1 FROM harness_candidates WHERE id = ?1\",\n                        [&alias_id],\n                        |_| Ok(()),\n                    )\n                    .optional()?\n                    .is_some()\n            {\n                anyhow::bail!(\"candidate alias integrity verification failed\");\n            }\n        }\n        tx.commit()?;\n        Ok(())\n    }\n\n    fn ensure_session_board_columns(&self) -> Result<()> {\n        if !self.has_column(\"session_board\", \"row_label\")? {\n            self.conn\n                .execute(\"ALTER TABLE session_board ADD COLUMN row_label TEXT\", [])\n                .context(\"Failed to add row_label column to session_board table\")?;\n        }\n\n        if !self.has_column(\"session_board\", \"previous_lane\")? {\n            self.conn\n                .execute(\n                    \"ALTER TABLE session_board ADD COLUMN previous_lane TEXT\",\n                    [],","sourceCodeStart":967,"sourceCodeEnd":1003,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/session/store.rs#L967-L1003","documentation":"Raised by ensure_harness_candidate_aliases in ecc2/src/session/store.rs:985 during the schema/migration integrity pass. For each row in harness_candidate_aliases it reconstructs the CandidateSpec from harness_candidates, recomputes legacy_id and id_for_v2, and bails if the alias row's stored version/target/alias triplet disagrees with the recomputed values, or if an alias_id collides with an existing primary id in harness_candidates. This is a defensive check that the persisted alias table matches deterministic id derivation.","triggerScenarios":"Running a migration against a database whose harness_candidates rows were hand-edited, partially restored from backup, or produced by an older CandidateSpec id-derivation algorithm whose legacy_id/id_for_v2 outputs no longer match the rows on disk; a collision where an alias_id equals a primary candidate id (alias pointing at itself).","commonSituations":"Downgrading the binary after an id-format upgrade (v1 -> v2 alias scheme changed); restoring a DB snapshot from a different build; concurrent writers from two ecc2 versions with different id-derivation logic; manual SQL edits to harness_candidate_aliases.","solutions":["Back up the sqlite DB, then re-run the migration; if it fails, inspect harness_candidate_aliases vs harness_candidates for the offending alias_id and reconcile the row.","Ensure all ecc2 instances sharing the DB are on the same version so CandidateSpec::id_for_v2 and legacy_id produce identical results.","If the DB was partially restored, drop and rebuild harness_candidate_aliases from harness_candidates by re-registering aliases via register_harness_alias (the code path at store.rs:945).","Stop hand-editing candidate/alias rows; route all writes through the CandidateSpec API."],"exampleFix":"// before: legacy and v2 ids disagree after a partial restore\n// harness_candidate_aliases row: alias_id=\"abc-v2\", candidate_id=\"abc\", version=1\n// -> ensure_harness_candidate_aliases bails\n\n// after: rebuild aliases deterministically from candidates\nfor candidate in store.list_candidates()? {\n    let legacy = candidate.legacy_id();\n    let v2 = candidate.id_for_v2()?;\n    if legacy != v2 {\n        store.register_harness_alias(&tx, &v2, &legacy)?;\n    }\n}","handlingStrategy":"validation","validationCode":"// Before opening a DB that may have been produced by another build, run a\n// read-only integrity probe (do not call the destructive migration path).\nfn probe_alias_integrity(conn: &rusqlite::Connection) -> anyhow::Result<()> {\n    let mut stmt = conn.prepare(\n        \"SELECT alias_id, candidate_id, id_version FROM harness_candidate_aliases\",\n    )?;\n    let rows: Vec<(String,String,i64)> = stmt.query_map([], |r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)))?\n        .collect::<rusqlite::Result<_>>()?;\n    for (alias_id, candidate_id, version) in rows {\n        if version != 2 { anyhow::bail!(\"alias {alias_id} has non-v2 version {version}\"); }\n        if alias_id == candidate_id { anyhow::bail!(\"alias {alias_id} points at itself\"); }\n    }\n    Ok(())\n}\n\n// Call probe_alias_integrity on a COPY of the DB first; only run the real\n// migration if the probe passes.","typeGuard":"// Not applicable: this is a data-integrity predicate over DB rows, not a\n// language type. Encode it as a function returning Result (above).","tryCatchPattern":"// Treat migration-time integrity failure as fatal and surface the offending\n// alias row so the operator can reconcile manually rather than lose data.\nmatch store.run_migrations() {\n    Ok(()) => Ok(()),\n    Err(e) if e.to_string().contains(\"alias integrity verification failed\") => {\n        eprintln!(\"FATAL: harness_candidate_aliases is inconsistent. Back up the DB and reconcile.\");\n        std::process::exit(1);\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Run ecc2 on a single version across all writers sharing a DB; id-derivation changes between versions break alias integrity.","Back up the sqlite file before any upgrade that touches harness_candidates.","Never edit harness_candidate_aliases or harness_candidates rows by hand.","After a restore, rebuild aliases via register_harness_alias from the candidates."],"tags":["database","migration","integrity","sqlite","harness-candidate","alias"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}