affaan-m/ECC · error · ValueError
analyze() needs at least one frame
Error message
analyze() needs at least one frame
What it means
`analyze()` distills grade statistics from a list of float32 RGB frames and raises a ValueError when the list is empty. Statistics like mean Lab values, chroma, and contrast are undefined without pixels, so the function fails fast instead of producing NaNs. It is called by neutral_stats, grade_clip_adaptive, and grade_clip_direct.
Solutions
- Check `len(frames) > 0` before calling analyze().
- Fix the upstream frame source: ensure `sample_frames()` returns frames (see its 'decoded zero frames' error).
- For adaptive flows, fall back to a direct LUT grade when no frames can be measured.
- In tests, seed analyze with at least one valid frame array.
Example fix
// before
stats = analyze(frames) # frames == []
// after
if not frames:
frames = _frames.sample_frames(src, n=24)
if not frames:
raise RuntimeError(f"no frames to analyze from {src}")
stats = analyze(frames) Defensive patterns
Strategy: validation
Validate before calling
if not frames or not all(isinstance(f, np.ndarray) and f.ndim == 3 for f in frames):
raise ValueError('analyze() requires a non-empty list of frame arrays') Type guard
def has_frames(frames: list[np.ndarray]) -> bool:
return len(frames) > 0 and all(isinstance(f, np.ndarray) and f.size > 0 for f in frames) Try / catch
try:
stats = analyze(frames)
except ValueError as e:
if 'at least one frame' in str(e):
stats = DEFAULT_GRADE_STATS # neutral fallback
else:
raise Prevention
- Never call analyze() without a length check on the frame list
- Sanity-check sample_frames() output before analysis
- Provide a neutral default stats object for degenerate clips
- Add a unit test for analyze([]) in downstream wrappers
When it happens
Trigger: Calling `analyze([])` — typically the result of sampling zero frames from a broken video or filtering frames with a mask that removed everything.
Common situations: Passing the output of `sample_frames()` on an empty/corrupt video; building the frame list inside a loop whose condition never appended; an upstream filter dropping all frames before analysis.
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
- expected (N, 3) pixels, got
- merge_videos() needs at least one video URL
- nothing to concatenate
- analyze() needs at least one frame
- asset fps differs from timeline
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/95f66819fff6bfc7.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/taste/grade.py:214
populated = [i for i, v in enumerate(raw) if v is not None]
if not populated:
g = [float(a.mean()), float(a.std()), float(b.mean()), float(b.std())]
return [list(g) for _ in range(_N_ZONES)]
out: list[list] = []
for z in range(_N_ZONES):
if raw[z] is not None:
out.append(raw[z])
else:
nearest = min(populated, key=lambda p: abs(p - z))
out.append(list(raw[nearest]))
return out
def analyze(frames: list[np.ndarray]) -> GradeStats:
"""Distill grade statistics from a list of float32 RGB frames in [0,1]."""
if not frames:
raise ValueError("analyze() needs at least one frame")
labs = [_to_lab(f) for f in frames]
stacked = np.concatenate([l.reshape(-1, 3) for l in labs], axis=0)
L, a, b = stacked[:, 0], stacked[:, 1], stacked[:, 2]
chroma = np.sqrt(a.astype(np.float64) ** 2 + b.astype(np.float64) ** 2)
return GradeStats(
zones=_zone_stats(L, a, b),
lab_mean=[float(L.mean()), float(a.mean()), float(b.mean())],
lab_std=[float(L.std()), float(a.std()), float(b.std())],
l_cdf=[float(v) for v in _cdf_of_l(L)],
black_point=float(np.percentile(L, 1)),
white_point=float(np.percentile(L, 99)),
contrast=float(L.std()),
saturation=float(chroma.mean()),
warmth=float(b.mean()),
tint=float(a.mean()),View on GitHub (pinned to 8321021c54)