affaan-m/ECC · error · FalError

merge_videos() needs at least one video URL

Error message

merge_videos() needs at least one video URL

What it means

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.

Solutions

  1. Ensure at least one generated clip URL exists before calling merge_videos
  2. Fix upstream video generation steps that returned no URLs
  3. Fail fast in the caller: check len(video_urls) > 0 and raise a descriptive error
  4. If clips are conditionally produced, skip the merge step when only zero-or-one clip exists

Example fix

// before
clips = [g for g in generate_all() if g.ok]
falapi.merge_videos([c.url for c in clips])  # FalError when all failed
// after
clip_urls = [c.url for c in clips]
if not clip_urls:
    raise RuntimeError("merge_videos skipped: no clips were generated")
merged = falapi.merge_videos(clip_urls)
Defensive patterns

Strategy: validation

Validate before calling

if not video_urls:
    raise ValueError("merge_videos skipped: no video URLs available")

Type guard

def can_merge(video_urls) -> bool:
    return isinstance(video_urls, list) and len(video_urls) > 0

Try / catch

try:
    merged = falapi.merge_videos(urls)
except falapi.FalError as e:
    if "at least one video" in str(e):
        print("No clips produced upstream; skipping merge")

Prevention

When it happens

Trigger: merge_videos([]) — typically because upstream video-generation steps produced no URLs (failures collected into an empty list).

Common situations: 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.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-distillation/scripts/taste/falapi.py:647

    result = submit(ENDPOINTS["extract_frame"], payload, timeout)
    return first_url(result, ENDPOINTS["extract_frame"])


def compose(tracks: list[dict], *, timeout: int = 900) -> str:
    """Composite timeline tracks into one video. Returns the output URL.

    ``tracks`` is passed straight through so the caller owns the timeline
    shape; the ffmpeg-api track schema is another default worth verifying
    before a live run.
    """
    result = submit(ENDPOINTS["compose"], {"tracks": tracks}, timeout)
    return first_url(result, ENDPOINTS["compose"])


def merge_videos(video_urls: list[str], *, timeout: int = 900) -> str:
    """Concatenate videos end to end. Returns the merged URL."""
    if not video_urls:
        raise FalError("merge_videos() needs at least one video URL")
    payload = {"video_urls": list(video_urls)}
    result = submit(ENDPOINTS["merge_videos"], payload, timeout)
    return first_url(result, ENDPOINTS["merge_videos"])


# ---------------------------------------------------------------------------
# download
# ---------------------------------------------------------------------------


MAX_DOWNLOAD_BYTES = 2 * 1024 * 1024 * 1024  # bounded large video/GLB downloads


def _validate_download_url(url: str) -> None:
    try:
        parsed = urllib.parse.urlsplit(url)
        host = parsed.hostname or ""
        valid = (parsed.scheme == "https" and not parsed.username

View on GitHub (pinned to 8321021c54)