affaan-m/ECC · error
at least one non-empty
Error message
at least one non-empty {kind} reference is required What it means
normalize_refs validates that the trace_refs/evidence_refs passed to HarnessCandidate::new contain at least one entry and that no entry is blank or whitespace-only. The library requires every candidate to carry at least one non-empty reference for its content address. An empty vector or a list containing "" or " " triggers this error, with {kind} substituted by the parameter name (trace/evidence).
Solutions
- Supply at least one meaningful (non-blank) reference in the refs vector before calling new.
- Filter out blank entries before constructing: refs.into_iter().map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect().
- Fix the source config/script so the refs key is populated (e.g. ensure the artifact path or commit sha is generated).
- Validate inputs at the call site with an early check that fails with a clearer message naming the missing ref kind.
Example fix
// before
let candidate = HarnessCandidate::new(vec![], evidence_refs)?;
// after
let trace_refs: Vec<String> = collected
.into_iter()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
if trace_refs.is_empty() {
anyhow::bail!("no trace refs collected before building candidate");
}
let candidate = HarnessCandidate::new(trace_refs, evidence_refs)?; Defensive patterns
Strategy: validation
Validate before calling
fn valid_refs(refs: &[String]) -> bool {
!refs.is_empty() && refs.iter().all(|r| !r.trim().is_empty())
} Type guard
fn non_empty_refs(refs: Vec<String>) -> Option<Vec<String>> {
let cleaned: Vec<String> = refs.into_iter().map(|r| r.trim().to_string()).filter(|r| !r.is_empty()).collect();
(!cleaned.is_empty()).then_some(cleaned)
} Try / catch
match HarnessCandidate::new(trace_refs, evidence_refs) {
Ok(c) => proceed(c),
Err(e) if e.to_string().contains("non-empty") => bail!("trace/evidence refs missing from config"),
Err(e) => return Err(e),
} Prevention
- Trim and filter blank entries out of ref lists at the point of collection.
- Fail fast in config loading if trace_refs/evidence_refs keys are absent.
- Check pipeline step output is non-empty before building a candidate in CI.
When it happens
Trigger: HarnessCandidate::new called with trace_refs: vec![] or evidence_refs: vec![]; passing a list containing an empty string or whitespace-only entry such as vec![String::new()] or vec![" ".into()]; programmatic construction from a config where a refs key was missing and defaulted to empty.
Common situations: Config file omits trace_refs/evidence_refs so an empty Vec is passed; a CI pipeline builds the refs list dynamically and a step produced no output; splitting a comma-separated string of refs yields an empty trailing element.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- image_to_3d needs at least one image
- merge_videos() needs at least one video URL
- at least one explicit seed is required
- At least one guided harness must be selected
- at least one placement is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/e2d925c2e0cb6a81.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/harness_eval.rs:261
Ok(Self::new(
serde_json::from_str(&self.canonical_config)?,
self.trace_refs.clone(),
self.evidence_refs.clone(),
)?
.id)
}
}
#[derive(Serialize)]
struct CanonicalCandidateArtifact<'a> {
config: Value,
trace_refs: &'a [String],
evidence_refs: &'a [String],
}
fn normalize_refs(kind: &str, refs: Vec<String>) -> Result<Vec<String>> {
if refs.is_empty() || refs.iter().any(|reference| reference.trim().is_empty()) {
bail!("at least one non-empty {kind} reference is required");
}
if refs.len() > 100 || refs.iter().any(|reference| reference.len() > 4096) {
bail!("{kind} references exceed bounded limits");
}
let mut normalized = refs
.into_iter()
.map(|reference| reference.trim().to_string())
.collect::<Vec<_>>();
normalized.sort();
normalized.dedup();
Ok(normalized)
}
fn sha256_hex(bytes: &[u8]) -> String {
Sha256::digest(bytes)
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()View on GitHub (pinned to 8321021c54)