affaan-m/ECC · error

recorded evidence exceeds bounded score or candidate limits

Error message

recorded evidence exceeds bounded score or candidate limits

What it means

from_evidence constructs a recorded evaluator from RecordedEvidence and enforces bounded, well-formed input. This error is thrown when the evidence contains more than 20,000 total recorded scores or any candidate id in the scores map is not exactly 64 characters long. The library enforces these bounds to keep memory usage bounded and to ensure candidate ids are sha256-style hex identifiers.

Solutions

  1. Check the recorded evidence before conversion: total score count must be <= 20000 and every key in evidence.scores must be exactly 64 characters.
  2. Regenerate candidate ids as proper 64-character hashes (e.g. sha256 hex) when recording evidence.
  3. Split or trim the evidence so it covers at most 20000 recorded scores, or run multiple evidence files.
  4. Verify the tool/version that produced the evidence file matches the schema this library expects.

Example fix

// before
let mut evidence = RecordedEvidence::default();
for i in 0..30000 {
    evidence.scores.entry(format!("cand-{i}")).or_default();
}
let eval = RecordedEvaluator::from_evidence(evidence)?; // bails

// after
let evidence = build_evidence_with_hash_ids_max_20k_scores();
assert!(evidence.scores.len() <= 20_000);
assert!(evidence.scores.keys().all(|id| id.len() == 64));
let eval = RecordedEvaluator::from_evidence(evidence)?;
Defensive patterns

Strategy: validation

Validate before calling

let score_count: usize = evidence.scores.values().map(|s| s.len()).sum();
if score_count > 20_000 || evidence.scores.keys().any(|id| id.len() != 64) {
    return Err("evidence exceeds bounded score/candidate limits");
}

Type guard

fn is_valid_candidate_id(id: &str) -> bool { id.len() == 64 }

Prevention

When it happens

Trigger: Calling RecordedEvaluator::from_evidence with a RecordedEvidence whose scores map contains more than 20,000 (candidate_id, seed) entries in total, or whose scores keys include a candidate id whose length differs from exactly 64 characters.

Common situations: Generating recorded evidence programmatically with placeholder or short candidate ids instead of real 64-char hashes; aggregating scores from a huge multi-candidate sweep without truncation; deserializing an evidence file produced by a different tool version that used shorter ids.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/ff3131b552b82289. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/harness_eval.rs:380

impl RecordedEvaluator {
    #[cfg(test)]
    pub fn new(scores: BTreeMap<(String, u64), f64>, health_ok: bool) -> Self {
        Self {
            name: "recorded-v1".into(),
            scores,
            health_ok,
            health_candidate: None,
            calls: Vec::new(),
        }
    }

    pub fn from_evidence(evidence: RecordedEvidence) -> Result<Self> {
        if evidence.evaluator != "recorded-v1" {
            bail!("CLI evidence evaluator must be recorded-v1");
        }
        let score_count = evidence.scores.values().map(BTreeMap::len).sum::<usize>();
        if score_count > 20_000 || evidence.scores.keys().any(|id| id.len() != 64) {
            bail!("recorded evidence exceeds bounded score or candidate limits");
        }
        if evidence.health.len() != 1 {
            bail!("exactly one candidate-keyed health assertion is required");
        }
        let (health_candidate, health_ok) = evidence
            .health
            .into_iter()
            .next()
            .context("candidate-keyed health evidence is required")?;
        let scores = evidence
            .scores
            .into_iter()
            .flat_map(|(id, values)| {
                values
                    .into_iter()
                    .map(move |(seed, score)| ((id.clone(), seed), score))
            })
            .collect();

View on GitHub (pinned to 8321021c54)