{"record":{"id":"dc39be10c51a4d80","repo":"affaan-m/ECC","slug":"merge-videos-needs-at-least-one-video-url-dc39be","errorCode":null,"errorMessage":"merge_videos() needs at least one video URL","messagePattern":"merge_videos\\(\\) needs at least one video URL","errorType":"validation","errorClass":"FalError","httpStatus":null,"severity":"error","filePath":"skills/taste-distillation/scripts/taste/falapi.py","lineNumber":647,"sourceCode":"    result = submit(ENDPOINTS[\"extract_frame\"], payload, timeout)\n    return first_url(result, ENDPOINTS[\"extract_frame\"])\n\n\ndef compose(tracks: list[dict], *, timeout: int = 900) -> str:\n    \"\"\"Composite timeline tracks into one video. Returns the output URL.\n\n    ``tracks`` is passed straight through so the caller owns the timeline\n    shape; the ffmpeg-api track schema is another default worth verifying\n    before a live run.\n    \"\"\"\n    result = submit(ENDPOINTS[\"compose\"], {\"tracks\": tracks}, timeout)\n    return first_url(result, ENDPOINTS[\"compose\"])\n\n\ndef merge_videos(video_urls: list[str], *, timeout: int = 900) -> str:\n    \"\"\"Concatenate videos end to end. Returns the merged URL.\"\"\"\n    if not video_urls:\n        raise FalError(\"merge_videos() needs at least one video URL\")\n    payload = {\"video_urls\": list(video_urls)}\n    result = submit(ENDPOINTS[\"merge_videos\"], payload, timeout)\n    return first_url(result, ENDPOINTS[\"merge_videos\"])\n\n\n# ---------------------------------------------------------------------------\n# download\n# ---------------------------------------------------------------------------\n\n\nMAX_DOWNLOAD_BYTES = 2 * 1024 * 1024 * 1024  # bounded large video/GLB downloads\n\n\ndef _validate_download_url(url: str) -> None:\n    try:\n        parsed = urllib.parse.urlsplit(url)\n        host = parsed.hostname or \"\"\n        valid = (parsed.scheme == \"https\" and not parsed.username","sourceCodeStart":629,"sourceCodeEnd":665,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-distillation/scripts/taste/falapi.py#L629-L665","documentation":"merge_videos in the taste falapi client composites a list of already-generated video URLs via the fal merge endpoint; an empty video_urls list leaves nothing to merge and is rejected before submitting.","triggerScenarios":"merge_videos([]) — typically because upstream video-generation steps produced no URLs (failures collected into an empty list).","commonSituations":"Caller gathers generated clip URLs but all generation steps failed or were filtered out; forgot to pass the list; passing an empty list after filtering broken URLs.","solutions":["Ensure at least one generated clip URL exists before calling merge_videos","Fix upstream video generation steps that returned no URLs","Fail fast in the caller: check len(video_urls) > 0 and raise a descriptive error","If clips are conditionally produced, skip the merge step when only zero-or-one clip exists"],"exampleFix":"// before\nclips = [g for g in generate_all() if g.ok]\nfalapi.merge_videos([c.url for c in clips])  # FalError when all failed\n// after\nclip_urls = [c.url for c in clips]\nif not clip_urls:\n    raise RuntimeError(\"merge_videos skipped: no clips were generated\")\nmerged = falapi.merge_videos(clip_urls)","handlingStrategy":"validation","validationCode":"if not video_urls:\n    raise ValueError(\"merge_videos skipped: no video URLs available\")","typeGuard":"def can_merge(video_urls) -> bool:\n    return isinstance(video_urls, list) and len(video_urls) > 0","tryCatchPattern":"try:\n    merged = falapi.merge_videos(urls)\nexcept falapi.FalError as e:\n    if \"at least one video\" in str(e):\n        print(\"No clips produced upstream; skipping merge\")","preventionTips":["Count successfully generated clips before the merge stage","Fail loudly when generation stages yield zero outputs instead of passing empty lists","Skip merge entirely when fewer than 2 clips exist (nothing to concatenate)","Collect and report per-stage failures so empty lists are diagnosable"],"tags":["python","validation","argument"],"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"}