affaan-m/ECC · error · ValueError
apply_local needs at least one media clip
Error message
apply_local needs at least one media clip
What it means
apply_local plans a cut over the pack's cadence and needs at least one local media clip to slice; an empty media list leaves nothing to place on the timeline, so it fails rather than producing an empty plan.
Solutions
- Pass at least one media clip: `apply_local(media=[{"path": "clip.mp4", "duration": 2.0}], ...)`
- Check the media directory path and glob pattern used to build the list
- Log/dump the media list after any filtering to confirm it is not emptied before apply_local
- Handle empty media in the caller by failing early with a user-facing message instead of calling apply_local
Example fix
# before
apply_local(media=[], sp=sp, target=10.0, fps=30)
# after
media = load_clips("assets/media/") # verify non-empty
if not media:
raise SystemExit("no media clips found")
apply_local(media=media, sp=sp, target=10.0, fps=30) Defensive patterns
Strategy: validation
Validate before calling
if not media:
raise SystemExit("apply_local: media list is empty; check your media directory") Type guard
def has_media(clips) -> bool:
return isinstance(clips, list) and len(clips) > 0 Try / catch
try:
report = apply_local(media=media, sp=sp, target=target, fps=fps)
except ValueError as exc:
if "at least one media clip" in str(exc):
raise SystemExit("Stage media files into the pack before applying.") from exc
raise Prevention
- Check the media glob/path finds files before invoking apply_local
- Dump the media list length after any filtering/dedup step
- Fail early in your CLI when a media directory is missing or empty
When it happens
Trigger: `apply_local(media=[], ...)` or a media list that became empty after filtering (e.g. all clips removed during normalization or dedup).
Common situations: A media directory that failed to be scanned/globbed (wrong path, no matching extensions); an upstream filter removing all clips; a user running a dry run without staging media first.
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
- asset contains too few frames
- asset requires verified alpha
- candidate fps/duration/retime ambiguity
- Crop must contain four finite values
- FileNotFoundError(source)
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/0db0925b9a8500a6.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/apply.py:155
media: list[dict[str, Any]],
duration: float | None = None,
fps: float | None = None,
no_repeat: bool = False,
) -> dict[str, Any]:
"""Plan a cut from the pack's cadence over local media clips.
With ``no_repeat=True``, normalized source paths are used at most once;
insufficient sources or source durations fail instead of repeating clips.
Strict plans fill the nearest whole-frame target and never exceed source
capacity. Media duration metadata must describe the available source.
Returns an application report validated against
``schema.APPLICATION_REPORT_SCHEMA``. The report structurally cannot
claim a provider run: ``provider`` is enum-locked to ``"none"`` and
``dry_run`` to ``true``.
"""
if not media:
raise ValueError("apply_local needs at least one media clip")
if not sp.cadence_path.exists():
raise ValueError("pack has no measured cadence (cadence.json is missing)")
cadence = sp.read_json(sp.cadence_path)
if not isinstance(cadence, dict) or not (cadence.get("shots") or "mean_shot" in cadence):
raise ValueError("cadence.json has no measured shots to plan from")
seq_fps = _positive(fps if fps is not None else cadence.get("fps", _DEFAULT_FPS), "fps")
validated_media = []
for clip in media:
if not isinstance(clip, dict) or not isinstance(clip.get("path"), (str, Path)):
raise ValueError("media clips require a local source path")
if not str(clip["path"]).strip():
raise ValueError("media clips require a local source path")
validated_media.append({**clip, "duration": _positive(clip.get("duration"), "media duration")})
target = _positive(duration if duration is not None else sum(
c["duration"] for c in validated_media
), "target duration")
planned = plan_shots(cadence, target)View on GitHub (pinned to 8321021c54)