affaan-m/ECC · error
minimum win rate must be between 0 and 1
Error message
minimum win rate must be between 0 and 1
What it means
`PromotionPolicy::validate` requires `min_win_rate` to be finite and within the inclusive range 0.0..=1.0, since it represents a fraction of wins. Values like 1.5, -0.1, or NaN are statistically meaningless and are rejected.
Solutions
- Express the win rate as a fraction between 0.0 and 1.0 (0.85, not 85) before calling `compare`
- If your config uses percentages, divide by 100.0 at load time and validate the range
- Use `0.0..=1.0` `contains` checks in your own config loader to fail early with a clear message
Example fix
// before
let policy = PromotionPolicy { min_win_rate: 85.0, .. };
let comparison = policy.compare(&samples)?;
// after
let policy = PromotionPolicy { min_win_rate: 0.85, .. };
let comparison = policy.compare(&samples)?; Defensive patterns
Strategy: validation
Validate before calling
fn win_rate_is_valid(min_win_rate: f64) -> bool {
min_win_rate.is_finite() && (0.0..=1.0).contains(&min_win_rate)
} Type guard
fn fraction_f64(v: f64) -> Option<f64> {
if v.is_finite() && (0.0..=1.0).contains(&v) { Some(v) } else { None }
} Try / catch
match policy.compare(&samples) {
Ok(comparison) => comparison,
Err(e) if e.to_string().contains("win rate must be between 0 and 1") => {
eprintln!("min_win_rate out of range (did you pass a percentage?): {e}");
// retry assuming percentage input
PromotionPolicy { min_win_rate: policy.min_win_rate / 100.0, ..policy }.compare(&samples)?
}
Err(e) => return Err(e),
} Prevention
- Store win-rate thresholds strictly as fractions 0.0..=1.0 in config
- Convert percentages to fractions at the config boundary with a named helper
- Clamp or reject percent-style values (>1.0) in your config loader with a clear message
When it happens
Trigger: Constructing a `PromotionPolicy` with `min_win_rate` outside [0.0, 1.0] (e.g. 1.5 meaning '150%', a negative rate, or NaN) and calling `compare` or `validate`.
Common situations: Users expressing the threshold as a percentage (85 for 85%) instead of a fraction (0.85); copy-pasting thresholds from percent-based tooling; unset/NaN sentinels from config.
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
- Canonical session snapshot requires
- frame range is empty or outside its bounds
- minimum mean delta must be finite
- minimum samples must be positive
- -32602
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/7649881b0433c2e3.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/harness_eval.rs:514
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()
{
bail!("sample seeds must be unique");
}View on GitHub (pinned to 8321021c54)