affaan-m/ECC · error · RuntimeError

decoded zero frames from

Error message

decoded zero frames from {path}

What it means

sample_frames() raises when the video opens successfully but zero frames could actually be decoded (out list empty). This happens with headers that parse but whose stream data is unreadable, or when skip_edges trimming plus a tiny frame count leaves nothing to grab.

Solutions

  1. Verify the video decodes elsewhere (ffprobe -count_frames) to confirm corruption
  2. Re-encode with ffmpeg to repair the stream before sampling
  3. Reduce skip_edges or n so sampling covers the actual frame count for short clips
  4. Fall back to _sequential_sample logic by lowering requested frame counts

Example fix

// before
frames = sample_frames(tiny_clip, n=30, skip_edges=0.2)  # zero frames
// after
frames = sample_frames(tiny_clip, n=30, skip_edges=0.0) if frame_count <= 10 else sample_frames(tiny_clip, n=30, skip_edges=0.2)
Defensive patterns

Strategy: try-catch

Validate before calling

info = probe(path)
usable = max(0, int(info.frame_count * (1 - 2 * skip_edges)))
if usable <= 0: raise ValueError(f"clip too short to sample: {path}")

Try / catch

try:
    frames = sample_frames(path, n=12, skip_edges=0.1)
except RuntimeError as e:
    if "decoded zero frames" in str(e):
        frames = sample_frames(path, n=12, skip_edges=0.0)  # retry without trimming
    else: raise

Prevention

When it happens

Trigger: Calling sample_frames() on a video whose container opens but decodes no frames (corrupt stream, unsupported codec variant), or on a very short clip where requested n and skip_edges fraction exclude all frames.

Common situations: Interrupted encode/download leaving a valid header and truncated payload; 1-2 frame clips combined with a large skip_edges value; DRM or broken moov atom; live recordings with no decodable frames.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-distillation/scripts/taste/frames.py:81

        cap.release()
        return frames

    lo = int(total * skip_edges)
    hi = int(total * (1.0 - skip_edges))
    idxs = np.linspace(lo, max(lo + 1, hi - 1), num=min(n, max(1, hi - lo)))
    idxs = np.unique(idxs.astype(int))

    out: list[np.ndarray] = []
    for i in idxs:
        cap.set(cv2.CAP_PROP_POS_FRAMES, int(i))
        ok, bgr = cap.read()
        if not ok:
            continue
        out.append(_prep(bgr, max_edge))
    cap.release()

    if not out:
        raise RuntimeError(f"decoded zero frames from {path}")
    return out


def _sequential_sample(cap, n: int, max_edge: int) -> list[np.ndarray]:
    frames = []
    while True:
        ok, bgr = cap.read()
        if not ok:
            break
        frames.append(bgr)
    if not frames:
        return []
    idxs = np.unique(np.linspace(0, len(frames) - 1, num=min(n, len(frames))).astype(int))
    return [_prep(frames[i], max_edge) for i in idxs]


def _prep(bgr: np.ndarray, max_edge: int) -> np.ndarray:
    h, w = bgr.shape[:2]

View on GitHub (pinned to 8321021c54)