{"record":{"id":"6411d7df6367f3bd","repo":"affaan-m/ECC","slug":"no-clips-to-write-a-timeline-needs-at-least-one-event","errorCode":null,"errorMessage":"no clips to write - a timeline needs at least one event","messagePattern":"no clips to write - a timeline needs at least one event","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/taste/timeline.py","lineNumber":235,"sourceCode":"        if not path:\n            raise ValueError(f\"clip {i} has no 'path'\")\n        dur = float(c.get(\"duration\") or 0.0)\n        if dur <= 0:\n            raise ValueError(f\"clip {i} ({path}) has non-positive duration {dur!r}\")\n        frames = max(1, seconds_to_frames(dur, fps))  # never emit a zero-length event\n        name = str(c.get(\"name\") or Path(path).stem)\n        out.append(\n            {\n                \"path\": path,\n                \"name\": name,\n                \"frames\": frames,\n                \"offset_frames\": offset,\n                \"seconds\": dur,\n            }\n        )\n        offset += frames\n    if not out:\n        raise ValueError(\"no clips to write - a timeline needs at least one event\")\n    return out\n\n\ndef _file_uri(path: str) -> str:\n    \"\"\"Absolute ``file://`` URI. Works for paths that do not exist yet.\"\"\"\n    p = Path(path)\n    if not p.is_absolute():\n        p = Path.cwd() / p\n    # as_uri() percent-escapes correctly; normalise away '..' without resolving\n    # symlinks or requiring the file to exist.\n    return Path(str(p)).absolute().as_uri()\n\n\ndef _format_name(width: int, height: int, fps: float | Fraction) -> str:\n    f = fps_fraction(fps)\n    rate = float(f)\n    label = f\"{rate:.2f}\".rstrip(\"0\").rstrip(\".\").replace(\".\", \"\")\n    return f\"FFVideoFormat{height}p{label}\"","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/taste/timeline.py#L217-L253","documentation":"timeline.py raises ValueError('no clips to write - a timeline needs at least one event') in _normalise when the resulting normalized clip list is empty, i.e. the caller passed an empty clips list. A timeline with zero events cannot produce valid FCPXML/EDL.","triggerScenarios":"Calling build_fcpxml or build_edl (or write_timeline) with clips=[] or None-ish empty input.","commonSituations":"Upstream filter step removed all clips; file list was empty because the directory had no media; conditional logic skipped adding clips silently.","solutions":["Pass at least one valid clip dict with path and positive duration.","Check why the clips list is empty — log/inspect it before calling.","Guard at the call site: `if not clips: skip / raise earlier with context`.","Fix the upstream collection/filtering that produced an empty list."],"exampleFix":"// before\nbuild_fcpxml([])  # ValueError\n// after\nif not clips:\n    raise ValueError('no clips selected; nothing to build')\nbuild_fcpxml(clips, fps=30)","handlingStrategy":"validation","validationCode":"def require_non_empty(clips):\n    if not clips:\n        raise ValueError('no clips to write - a timeline needs at least one event')\n    return clips","typeGuard":"def has_clips(clips) -> bool:\n    return isinstance(clips, list) and len(clips) > 0","tryCatchPattern":"try:\n    write_timeline(clips, out_path, fmt='fcpxml')\nexcept ValueError as e:\n    if 'no clips to write' in str(e):\n        logger.warning('nothing to build; skipping timeline output')\n        return None","preventionTips":["Check clip selection results before attempting output","Log the count of collected clips at each pipeline stage","Fail fast when a filter step empties the clip list","Treat an empty media directory as a pipeline error, not a silent skip"],"tags":["validation","timeline","empty-input"],"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"}