{"record":{"id":"035a18bc43b20843","repo":"affaan-m/ECC","slug":"media-clips-require-a-local-source-path","errorCode":null,"errorMessage":"media clips require a local source path","messagePattern":"media clips require a local source path","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/apply.py","lineNumber":166,"sourceCode":"\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)\n    assignments = _strict_assign(planned, validated_media, target, seq_fps) if no_repeat else [\n        (validated_media[i % len(validated_media)], max(1, timeline.seconds_to_frames(d, seq_fps)))\n        for i, d in enumerate(planned)\n    ]\n\n    shots: list[dict[str, Any]] = []\n    events: list[dict[str, Any]] = []\n    clock = 0.0\n    offset_frames = 0\n    for i, (clip, frames) in enumerate(assignments):\n        d = float(frames / timeline.fps_fraction(seq_fps)) if no_repeat else planned[i]","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/apply.py#L148-L184","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap each path in a dict: media=[{'path': 'clip.mp4'}].","Rename alternate keys ('src', 'file') to 'path' before calling apply_local.","Filter or convert non-dict entries in the media list before the call.","Ensure each clip's path value is a str or pathlib.Path, not bytes or None."],"exampleFix":"// before\napply_local(sp, media=[\"a.mp4\", \"b.mp4\"])\n\n// after\napply_local(sp, media=[{\"path\": \"a.mp4\"}, {\"path\": \"b.mp4\"}])","handlingStrategy":"type-guard","validationCode":"bad = [c for c in media if not (isinstance(c, dict) and isinstance(c.get('path'), (str, Path)))]\nif bad:\n    raise ValueError(f\"clips must be dicts with a 'path': {bad}\")","typeGuard":"def is_clip(c) -> bool:\n    return isinstance(c, dict) and isinstance(c.get(\"path\"), (str, Path))","tryCatchPattern":"try:\n    report = apply_local(sp, media=media)\nexcept ValueError as e:\n    if \"local source path\" in str(e):\n        media = normalize_clips(raw_media)  # wrap strings into {'path': ...}\n        report = apply_local(sp, media=media)\n    else:\n        raise","preventionTips":["Normalize external clip formats to {'path': ...} at ingestion boundaries.","Type-check media lists with a validator (pydantic/typing) before calling.","Keep one clip-record type shared across your tooling."],"tags":["input-validation","media-clips","wrong-type"],"backgroundTag":"invalid-argument-value","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"}