affaan-m/ECC · error · ValueError

media clips require a local source path

Error message

media clips require a local source path

What it means

Each media clip passed to apply_local must be a dict containing a 'path' key whose value is a str or Path. If a clip is not a dict, or its 'path' is missing or of the wrong type (e.g. a plain string instead of {'path': ...}), this ValueError is raised. It enforces the clip record shape before any planning happens.

Solutions

  1. Wrap each path in a dict: media=[{'path': 'clip.mp4'}].
  2. Rename alternate keys ('src', 'file') to 'path' before calling apply_local.
  3. Filter or convert non-dict entries in the media list before the call.
  4. Ensure each clip's path value is a str or pathlib.Path, not bytes or None.

Example fix

// before
apply_local(sp, media=["a.mp4", "b.mp4"])

// after
apply_local(sp, media=[{"path": "a.mp4"}, {"path": "b.mp4"}])
Defensive patterns

Strategy: type-guard

Validate before calling

bad = [c for c in media if not (isinstance(c, dict) and isinstance(c.get('path'), (str, Path)))]
if bad:
    raise ValueError(f"clips must be dicts with a 'path': {bad}")

Type guard

def is_clip(c) -> bool:
    return isinstance(c, dict) and isinstance(c.get("path"), (str, Path))

Try / catch

try:
    report = apply_local(sp, media=media)
except ValueError as e:
    if "local source path" in str(e):
        media = normalize_clips(raw_media)  # wrap strings into {'path': ...}
        report = apply_local(sp, media=media)
    else:
        raise

Prevention

When it happens

Trigger: Calling apply_local(sp, media=["clip.mp4"]) with raw path strings, or media=[{'src': 'clip.mp4'}] with a wrong key name, or a non-dict element in the media list.

Common situations: Passing paths directly instead of clip dicts; using an alternate key like 'file' or 'src' from another tool's output format; a JSON config where media entries are strings; a None slipped into the media list.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/apply.py:166

    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)
    assignments = _strict_assign(planned, validated_media, target, seq_fps) if no_repeat else [
        (validated_media[i % len(validated_media)], max(1, timeline.seconds_to_frames(d, seq_fps)))
        for i, d in enumerate(planned)
    ]

    shots: list[dict[str, Any]] = []
    events: list[dict[str, Any]] = []
    clock = 0.0
    offset_frames = 0
    for i, (clip, frames) in enumerate(assignments):
        d = float(frames / timeline.fps_fraction(seq_fps)) if no_repeat else planned[i]

View on GitHub (pinned to 8321021c54)