affaan-m/ECC · error
candidate content address or canonical configuration is…
Error message
candidate content address or canonical configuration is invalid
What it means
`CandidateSpec::verify_integrity` recomputes the content address for the current canonical configuration and compares it with the stored/persisted `id`. If the stored id does not match the recomputed id (`self.id != self.id_for_v2()`), the candidate's identity and its content disagree — the record was corrupted, produced by a different algorithm version, or mutated after persistence. The error is a checksum-mismatch-style integrity failure surfacing at read/verification time.
Solutions
- Regenerate the candidate: rebuild the CandidateSpec from the current config via `CandidateSpec::new` and re-persist it so id and content are recomputed together.
- If the record is a legacy one, re-migrate it: compute `id_for_v2()` from its canonical_config and update the stored id (or use `legacy_id()` for old-scheme records).
- Diff the persisted canonical_config against what was originally submitted to find what mutated after the id was written.
- Do not hand-edit persisted candidate files; treat them as immutable content-addressed artifacts and delete+recreate instead.
Example fix
// before: stale id after config was edited on disk
let spec: CandidateSpec = load_candidate("candidates/abc.json")?;
spec.verify_integrity()?; // fails: id != id_for_v2()
// after: rebuild so identity matches content
let config = serde_json::from_str(&spec_raw.config)?;
let spec = CandidateSpec::new(config, spec_raw.trace_refs, spec_raw.evidence_refs)?;
spec.verify_integrity()?; // passes Defensive patterns
Strategy: try-catch
Validate before calling
fn integrity_ok(spec: &CandidateSpec) -> bool {
spec.id == spec.id_for_v2().unwrap_or_default()
} Type guard
fn is_valid_candidate(spec: &CandidateSpec) -> bool {
spec.verify_integrity().is_ok()
} Try / catch
match spec.verify_integrity() {
Ok(()) => use_spec(spec),
Err(e) if e.to_string().contains("content address") => {
// corrupted or legacy record: rebuild from source config
let rebuilt = rebuild_and_persist(&spec.canonical_config)?;
use_spec(&rebuilt)
}
Err(e) => return Err(e),
} Prevention
- Treat persisted candidate files as immutable; never edit canonical_config in place.
- Run a migration pass after upgrading hashing schemes (legacy_id -> v2 id) so old records match the new algorithm.
- Recompute the id in the same transaction/write as any content change.
- Verify integrity immediately after load, before using the candidate in evaluations.
When it happens
Trigger: Calling `verify_integrity()` (directly or via `record_harness_candidate`) on a persisted CandidateSpec whose `id` field does not equal `id_for_v2()` computed from its current `canonical_config`, `trace_refs`, and `evidence_refs`.
Common situations: Manually editing a persisted candidate JSON file after it was written; old records persisted with a legacy hashing scheme (`legacy_id` = sha256 of canonical config alone) being verified under the v2 scheme; a migration or partial write that changed canonical_config without recomputing the id; copying a candidate record between environments where content differed.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- harness health evidence integrity verification failed
- artifact SHA-256 does not match receipt
- artifact SHA-256 mismatch
- audit health evidence does not match its evaluation
- candidate id collision with different immutable content
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/6f2f2ad958b15d50.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/harness_eval.rs:217
}
let artifact = serde_json::to_string(&CanonicalCandidateArtifact {
config: serde_json::from_str(&canonical_config)?,
trace_refs: &trace_refs,
evidence_refs: &evidence_refs,
})?;
let id = sha256_hex(artifact.as_bytes());
Ok(Self {
id,
canonical_config,
trace_refs,
evidence_refs,
})
}
pub fn verify_integrity(&self) -> Result<()> {
self.verify_persisted_id(&self.id)?;
if self.id != self.id_for_v2()? {
bail!("candidate content address or canonical configuration is invalid");
}
Ok(())
}
pub fn legacy_id(&self) -> String {
sha256_hex(self.canonical_config.as_bytes())
}
pub fn verify_persisted_id(&self, persisted_id: &str) -> Result<()> {
let value: Value = serde_json::from_str(&self.canonical_config)?;
let rebuilt = Self::new(value, self.trace_refs.clone(), self.evidence_refs.clone())?;
let is_v1 = persisted_id == self.legacy_id();
let is_v2 = persisted_id == rebuilt.id;
if rebuilt.canonical_config != self.canonical_config
|| (!is_v1 && !is_v2)
|| (is_v2
&& (rebuilt.trace_refs != self.trace_refs
|| rebuilt.evidence_refs != self.evidence_refs))View on GitHub (pinned to 8321021c54)