affaan-m/ECC · error
health evidence does not match candidate and evaluator
Error message
health evidence does not match candidate and evaluator
What it means
Before evaluating a promotion, the store verifies the supplied HealthEvidenceSnapshot (signature/integrity via verify()) and checks that its candidate_id and evaluator fields match the promotion call's arguments. A mismatch means the health snapshot attests a different candidate or evaluator than the promotion being attempted, so the operation is refused to keep evidence and action aligned.
Solutions
- Regenerate the HealthEvidenceSnapshot for the exact candidate_id and evaluator being promoted in this call.
- Compare health_evidence.candidate_id and health_evidence.evaluator against your arguments before calling to catch mismatches early.
- If promoting multiple candidates, keep snapshots keyed per (candidate_id, evaluator) rather than sharing one snapshot.
- Ensure verify() passes first; a failed verify indicates tampering/corruption and needs a fresh snapshot, not a field patch.
Example fix
// before: snapshot from previous run for another candidate let snapshot = previous_snapshot; // candidate_id = "old..." store.evaluate_promote_and_health_check(&new_cand, &base, "recorded-v1", ..., &snapshot, ...)?; // after let snapshot = HealthEvidenceSnapshot::build(&new_cand, "recorded-v1", asserted_healthy)?; snapshot.verify()?; store.evaluate_promote_and_health_check(&new_cand, &base, "recorded-v1", ..., &snapshot, ...)?;
Defensive patterns
Strategy: validation
Validate before calling
// Verify snapshot alignment before calling the API health_evidence.verify()?; anyhow::ensure!(health_evidence.candidate_id == candidate_id, "snapshot is for a different candidate"); anyhow::ensure!(health_evidence.evaluator == evaluator, "snapshot is for a different evaluator");
Type guard
fn matches_promotion(snapshot: &HealthEvidenceSnapshot, candidate_id: &str, evaluator: &str) -> bool {
snapshot.candidate_id == candidate_id && snapshot.evaluator == evaluator
} Try / catch
match store.evaluate_promote_and_health_check(...) {
Err(e) if e.to_string().contains("health evidence does not match") => {
// rebuild the snapshot for this candidate/evaluator and retry once
}
other => other?,
} Prevention
- Create HealthEvidenceSnapshot fresh for every promotion call; never share across candidates
- Key snapshots by (candidate_id, evaluator) in caches
- Always call verify() on the snapshot yourself before invoking the API
When it happens
Trigger: Calling evaluate_promote_and_health_check with a health_evidence snapshot built for a different candidate_id or evaluator string than the arguments passed in, or with a snapshot that fails verify() (raising the verify error, not this one).
Common situations: Reusing a cached health snapshot from a previous evaluation of another candidate; swapping the evaluator label after the snapshot was created (e.g. changing "recorded-v1" casing or suffix); copy-paste mistakes when wiring multiple promotions in parallel.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- exactly one candidate-keyed health assertion is required
- health check result does not match persisted assertion
- candidate configuration exceeds 1 MiB
- effect lacks reference evidence
- effect evidence source duration is not bound to its receipt…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/37dfa159514df3bb.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/session/store.rs:5476
policy: PromotionPolicy,
evidence_ref: &str,
health_evidence: &HealthEvidenceSnapshot,
health_check: F,
) -> Result<HarnessPromotionOutcome>
where
F: FnOnce(&str) -> Result<bool>,
{
if candidate_id.len() != 64
|| baseline_id.len() != 64
|| evaluator != "recorded-v1"
|| evidence_ref.trim().is_empty()
|| evidence_ref.len() > 4096
{
anyhow::bail!("valid candidate ids, recorded-v1 evaluator, and bounded evidence reference are required");
}
health_evidence.verify()?;
if health_evidence.candidate_id != candidate_id || health_evidence.evaluator != evaluator {
anyhow::bail!("health evidence does not match candidate and evaluator");
}
let comparison = policy.compare(samples)?;
let tx = self.conn.unchecked_transaction()?;
let stored_candidate_id = Self::resolve_harness_candidate_id(&tx, candidate_id)?;
let stored_baseline_id = Self::resolve_harness_candidate_id(&tx, baseline_id)?;
let active: String = tx
.query_row(
"SELECT candidate_id FROM active_harness_config WHERE slot = 'default'",
[],
|row| row.get(0),
)
.context("no active baseline configuration")?;
if active != stored_baseline_id {
anyhow::bail!("baseline is not the active harness configuration");
}
let now = chrono::Utc::now().to_rfc3339();
if !comparison.passed {
tx.execute("INSERT INTO harness_evaluations (candidate_id, baseline_id, evaluator, samples_json, policy_json, comparison_json, evidence_ref, legacy_unverifiable, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, 0, ?8)", rusqlite::params![stored_candidate_id, stored_baseline_id, evaluator, serde_json::to_string(samples)?, serde_json::to_string(&policy)?, serde_json::to_string(&comparison)?, evidence_ref, now])?;View on GitHub (pinned to 8321021c54)