{"record":{"id":"bb7457ba41b7e229","repo":"affaan-m/ECC","slug":"nothing-to-concatenate","errorCode":null,"errorMessage":"nothing to concatenate","messagePattern":"nothing to concatenate","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/taste/assemble.py","lineNumber":181,"sourceCode":"        f\"[0:v]format=gbrp[base];\"\n        f\"[base][ov]blend=all_mode={blend or 'screen'}:shortest=1,format=yuv420p\"\n    )\n    _run([\n        \"ffmpeg\", \"-nostdin\", \"-loglevel\", \"error\", \"-y\",\n        # Keep the still alive until the video ends; shortest=1 otherwise\n        # terminates every shot after the image's single decoded frame.\n        \"-i\", str(clip), \"-loop\", \"1\", \"-i\", str(image), \"-filter_complex\", fc,\n        \"-c:v\", \"libx264\", \"-crf\", \"14\", \"-preset\", \"veryfast\",\n        \"-pix_fmt\", \"yuv420p\", \"-an\", str(dst),\n    ])\n    return Path(dst)\n\n\ndef concat(clips: list[str | Path], dst: str | Path, fps: float = 24.0) -> Path:\n    \"\"\"Join clips into one file. Assumes they already share codec and size.\"\"\"\n    clips = [Path(c) for c in clips]\n    if not clips:\n        raise ValueError(\"nothing to concatenate\")\n    dst = Path(dst)\n    dst.parent.mkdir(parents=True, exist_ok=True)\n    listing = dst.parent / f\"{dst.stem}_concat.txt\"\n    listing.write_text(\"\".join(f\"file '{c.resolve().as_posix()}'\\n\" for c in clips))\n    _run([\n        \"ffmpeg\", \"-nostdin\", \"-loglevel\", \"error\", \"-y\",\n        \"-f\", \"concat\", \"-safe\", \"0\", \"-i\", str(listing),\n        \"-vf\", f\"fps={fps:.6f}\",\n        \"-c:v\", \"libx264\", \"-crf\", \"16\", \"-pix_fmt\", \"yuv420p\", str(dst),\n    ])\n    listing.unlink(missing_ok=True)\n    return dst\n\n\ndef normalize(\n    src: str | Path,\n    dst: str | Path,\n    width: int,","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/taste/assemble.py#L163-L199","documentation":"concat() raises this ValueError when given an empty clips list. The function joins a list of clip paths into one file via ffmpeg's concat demuxer, which requires at least one entry in the generated listing file; an empty list would produce a useless/invalid concat manifest.","triggerScenarios":"Calling concat([], dst) — i.e. no clip paths supplied, typically when an upstream step (e.g. cut_take) produced zero output files and the result was passed through unchecked.","commonSituations":"A shot-cutting pass matched no segments so the clip list came back empty; a glob pattern matched nothing; filtering clips by a criteria that excluded everything before calling concat.","solutions":["Ensure at least one clip path is passed; validate the list length before calling concat()","Check the upstream step (cut_take) actually produced output files in dest_dir","Fix the glob/filter that produced the empty list","If an empty result is legitimate, handle it before calling concat rather than relying on this error"],"exampleFix":"// before\nconcat(clips, out)  # clips may be []\n// after\nif not clips:\n    raise RuntimeError(\"upstream cut produced no clips\")\nconcat(clips, out)","handlingStrategy":"validation","validationCode":"clips = [c for c in clips if Path(c).is_file()]\nif not clips:\n    raise RuntimeError(\"no valid clips to concatenate\")","typeGuard":"def has_clips(clips) -> bool:\n    from pathlib import Path\n    return bool(clips) and all(Path(c).is_file() for c in clips)","tryCatchPattern":"try:\n    concat(clips, dst)\nexcept ValueError as e:\n    if str(e) == \"nothing to concatenate\":\n        print(\"upstream pipeline produced no clips\")\n    raise","preventionTips":["Check the output of cut_take/clip-generation steps for an empty result before concat","Glob clip directories and assert at least one file matched","Fail fast at the pipeline boundary with a domain-specific message instead of letting concat raise"],"tags":["ffmpeg","validation","empty-input","video-processing"],"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"}