{"record":{"id":"0db0925b9a8500a6","repo":"affaan-m/ECC","slug":"apply-local-needs-at-least-one-media-clip","errorCode":null,"errorMessage":"apply_local needs at least one media clip","messagePattern":"apply_local needs at least one media clip","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/apply.py","lineNumber":155,"sourceCode":"    media: list[dict[str, Any]],\n    duration: float | None = None,\n    fps: float | None = None,\n    no_repeat: bool = False,\n) -> dict[str, Any]:\n    \"\"\"Plan a cut from the pack's cadence over local media clips.\n\n    With ``no_repeat=True``, normalized source paths are used at most once;\n    insufficient sources or source durations fail instead of repeating clips.\n    Strict plans fill the nearest whole-frame target and never exceed source\n    capacity. Media duration metadata must describe the available source.\n\n    Returns an application report validated against\n    ``schema.APPLICATION_REPORT_SCHEMA``. The report structurally cannot\n    claim a provider run: ``provider`` is enum-locked to ``\"none\"`` and\n    ``dry_run`` to ``true``.\n    \"\"\"\n    if not media:\n        raise ValueError(\"apply_local needs at least one media clip\")\n\n    if not sp.cadence_path.exists():\n        raise ValueError(\"pack has no measured cadence (cadence.json is missing)\")\n    cadence = sp.read_json(sp.cadence_path)\n    if not isinstance(cadence, dict) or not (cadence.get(\"shots\") or \"mean_shot\" in cadence):\n        raise ValueError(\"cadence.json has no measured shots to plan from\")\n    seq_fps = _positive(fps if fps is not None else cadence.get(\"fps\", _DEFAULT_FPS), \"fps\")\n    validated_media = []\n    for clip in media:\n        if not isinstance(clip, dict) or not isinstance(clip.get(\"path\"), (str, Path)):\n            raise ValueError(\"media clips require a local source path\")\n        if not str(clip[\"path\"]).strip():\n            raise ValueError(\"media clips require a local source path\")\n        validated_media.append({**clip, \"duration\": _positive(clip.get(\"duration\"), \"media duration\")})\n    target = _positive(duration if duration is not None else sum(\n        c[\"duration\"] for c in validated_media\n    ), \"target duration\")\n    planned = plan_shots(cadence, target)","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/apply.py#L137-L173","documentation":"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.","triggerScenarios":"`apply_local(media=[], ...)` or a media list that became empty after filtering (e.g. all clips removed during normalization or dedup).","commonSituations":"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.","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"],"exampleFix":"# before\napply_local(media=[], sp=sp, target=10.0, fps=30)\n# after\nmedia = load_clips(\"assets/media/\")  # verify non-empty\nif not media:\n    raise SystemExit(\"no media clips found\")\napply_local(media=media, sp=sp, target=10.0, fps=30)","handlingStrategy":"validation","validationCode":"if not media:\n    raise SystemExit(\"apply_local: media list is empty; check your media directory\")","typeGuard":"def has_media(clips) -> bool:\n    return isinstance(clips, list) and len(clips) > 0","tryCatchPattern":"try:\n    report = apply_local(media=media, sp=sp, target=target, fps=fps)\nexcept ValueError as exc:\n    if \"at least one media clip\" in str(exc):\n        raise SystemExit(\"Stage media files into the pack before applying.\") from exc\n    raise","preventionTips":["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"],"tags":["media","empty-list","validation"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}