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

FalError raised by merge_videos() when the video_urls argument is empty (None, [], or falsy). Concatenating videos requires at least one clip URL, and the library rejects the empty case before making a provider call.

Solutions

  1. Pass a non-empty list of video URLs to merge_videos
  2. Materialize any generator into a list first and check it is non-empty
  3. Trace upstream clip generation — if no clips were produced, fix that step
  4. Guard with an explicit emptiness check before calling

Example fix

// before
clips = render_clips()  # generator
url = merge_videos(list(clips))
// after
clips = list(render_clips())
if not clips:
    raise SystemExit("no clips rendered; nothing to merge")
url = merge_videos(clips)
Defensive patterns

Strategy: validation

Validate before calling

clips = list(video_urls) if video_urls else []
if not clips or not all(isinstance(u, str) and u.startswith("https://") for u in clips):
    raise SystemExit("merge_videos needs >=1 valid https video URL")

Try / catch

try:
    merged = merge_videos(clips)
except FalError:
    logging.error("merge_videos called with empty list; clips=%r", clips)
    raise

Prevention

When it happens

Trigger: Calling merge_videos([]) or merge_videos(video_urls=None); passing a list that was emptied by upstream filtering; passing a generator/iterable that was already consumed so it converts to an empty list.

Common situations: Downstream of a render loop that produced zero clips; passing a one-shot iterator (e.g. a map/generator) that list() drains to [] on the second read; variable initialized to [] and never filled due to an earlier error path.

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/09f4019ee4c1c10a. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/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)