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

  1. Pass at least one media clip: `apply_local(media=[{"path": "clip.mp4", "duration": 2.0}], ...)`
  2. Check the media directory path and glob pattern used to build the list
  3. Log/dump the media list after any filtering to confirm it is not emptied before apply_local
  4. 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

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


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)