affaan-m/ECC · error
minimum mean delta must be finite
Error message
minimum mean delta must be finite
What it means
`PromotionPolicy::validate` requires `min_mean_delta` to be a finite number (not NaN or infinity). A non-finite threshold would make every comparison degenerate (always pass or always fail), so the policy is rejected.
Solutions
- Set `min_mean_delta` to a finite threshold (e.g. 0.05) before calling `compare`
- Replace NaN/inf sentinels with `Option<f64>` in your config layer and validate before constructing the policy
- Check the computation producing the threshold for division by zero or overflow
Example fix
// before
let policy = PromotionPolicy { min_mean_delta: f64::NAN, .. };
let comparison = policy.compare(&samples)?;
// after
let policy = PromotionPolicy { min_mean_delta: 0.05, .. };
let comparison = policy.compare(&samples)?; Defensive patterns
Strategy: validation
Validate before calling
fn delta_threshold_is_valid(min_mean_delta: f64) -> bool {
min_mean_delta.is_finite()
} Type guard
fn finite_f64(v: f64) -> Option<f64> {
if v.is_finite() { Some(v) } else { None }
} Try / catch
match policy.compare(&samples) {
Ok(comparison) => comparison,
Err(e) if e.to_string().contains("mean delta must be finite") => {
eprintln!("non-finite min_mean_delta in policy config: {e}");
Err(anyhow::anyhow!("fix min_mean_delta in promotion config"))
}
Err(e) => return Err(e),
} Prevention
- Model optional thresholds as Option<f64>, never NaN sentinels
- Check divisions producing the threshold for zero denominators
- Validate all f64 config fields with is_finite() at load time
When it happens
Trigger: Building a `PromotionPolicy` with `min_mean_delta` set to `f64::NAN`, `f64::INFINITY`, or a value computed from a division by zero, then calling `compare` or `validate`.
Common situations: Thresholds read from config where a sentinel 'unset' value of NaN/inf is used; computing the delta threshold as `a / b` with `b == 0.0`; JSON deserialization of a float that overflowed.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- minimum samples must be positive
- minimum win rate must be between 0 and 1
- -32602
- a claim token is required
- a confirmed nonempty coordinate is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/18211a4979d06eb4.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/harness_eval.rs:511
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Comparison {
pub passed: bool,
pub sample_count: usize,
pub candidate_mean: f64,
pub baseline_mean: f64,
pub mean_delta: f64,
pub win_rate: f64,
pub failures: Vec<String>,
}
impl PromotionPolicy {
pub fn validate(self) -> Result<()> {
if self.min_samples == 0 {
bail!("minimum samples must be positive");
}
if !self.min_mean_delta.is_finite() {
bail!("minimum mean delta must be finite");
}
if !self.min_win_rate.is_finite() || !(0.0..=1.0).contains(&self.min_win_rate) {
bail!("minimum win rate must be between 0 and 1");
}
Ok(())
}
pub fn compare(self, samples: &[PairedSample]) -> Result<Comparison> {
self.validate()?;
if samples.is_empty() {
bail!("samples cannot be empty");
}
if samples
.iter()
.map(|sample| sample.seed)
.collect::<BTreeSet<_>>()
.len()
!= samples.len()View on GitHub (pinned to 8321021c54)