affaan-m/ECC · error · anyhow::Error
baseline is not the active harness configuration
Error message
baseline is not the active harness configuration
What it means
promote_harness requires that the baseline_id argument equals the currently active candidate in slot 'default'. Promotion is always relative to the live baseline: you cannot promote against a stale or hypothetical baseline.
Source
Thrown at ecc2/src/session/store.rs:5490
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])?;
let evaluation_id = tx.last_insert_rowid();
tx.execute("INSERT INTO harness_eval_audit (event_type, candidate_id, prior_candidate_id, evaluation_id, evidence_ref, legacy_unverifiable, created_at) VALUES ('promotion_rejected', ?1, ?2, ?3, ?4, 0, ?5)", rusqlite::params![stored_candidate_id, stored_baseline_id, evaluation_id, evidence_ref, now])?;
tx.commit()?;
return Ok(HarnessPromotionOutcome {
evaluation_id: Some(evaluation_id),
promoted: false,
rolled_back: false,
failures: comparison.failures,
});
}
let changed = tx.execute("UPDATE active_harness_config SET candidate_id = ?1, updated_at = ?2 WHERE slot = 'default' AND candidate_id = ?3", rusqlite::params![stored_candidate_id, now, stored_baseline_id])?;
if changed != 1 {
anyhow::bail!("atomic promotion compare-and-swap failed");
}View on GitHub (pinned to 01e15490f0)
Solutions
- Re-read the active candidate id immediately before promoting and pass it as baseline_id.
- Serialize all promotions behind a single-writer / mutex so the baseline cannot change mid-call.
- Use resolve_harness_candidate_id to canonicalize both ids before comparing, so aliases do not cause false mismatches.
Example fix
// before
store.promote_harness(&candidate_id, &stale_baseline_id, ...)?;
// after
let live_baseline = store.active_harness_id()?.ok_or_else(|| anyhow::anyhow!("no active harness"))?;
store.promote_harness(&candidate_id, &live_baseline, ...)?; Defensive patterns
Strategy: validation
Validate before calling
let live_baseline = store.active_harness_id()? // or the store's active getter
.ok_or_else(|| anyhow::anyhow!("no active harness to promote against"))?;
if live_baseline != baseline_id {
// re-read happened; adopt the live baseline or abort
return Err(anyhow::anyhow!("baseline {baseline_id} is stale; live active is {live_baseline}"));
}
store.promote_harness(&candidate_id, &live_baseline, ...)?; Type guard
fn baseline_is_active(active: &str, baseline_id: &str) -> bool {
active == baseline_id
} Try / catch
match store.promote_harness(&candidate_id, &baseline_id, ...) {
Ok(out) => { /* handled */ }
Err(e) if e.to_string().contains("baseline is not the active") => {
// re-read active config and retry with the fresh baseline
}
Err(e) => return Err(e),
} Prevention
- Read the active baseline in the same critical section as the promotion call.
- Serialize promotions behind a single writer so the baseline cannot change mid-call.
- Resolve aliases to canonical ids before comparing baseline to active.
When it happens
Trigger: baseline_id (after resolve_harness_candidate_id) != the active_harness_config 'default' candidate_id. The active row is read with .context("no active baseline configuration") just before the comparison.
Common situations: Stale baseline captured before a prior promotion completed; a concurrent promotion already changed the active config; passing candidate_id as baseline_id by argument-order mistake.
Related errors
- atomic promotion compare-and-swap failed
- atomic rollback compare-and-swap failed
- candidate alias collision with physical candidate id
- candidate alias collision with different immutable target
- legacy candidate id collision with different immutable conte
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/0b4fbeafb3ec8a8d.
Report an issue: GitHub.