calesthio/OpenMontage · error · RuntimeError

Files API failed to process the uploaded video

Error message

Files API failed to process the uploaded video

What it means

Raised when the Google Files API reports state=FAILED for an uploaded video. The Files API validates and transcodes uploads asynchronously; if the media is rejected (corrupt, unsupported, or violates policy) the file transitions from PROCESSING to FAILED instead of ACTIVE, and the tool raises RuntimeError before ever using the file.

Source

Thrown at tools/video/gemini_omni_video.py:275

        )
        upload_resp.raise_for_status()
        file_info = upload_resp.json().get("file", {})

        # Wait until the uploaded video is processed before referencing it.
        deadline = time.time() + _MAX_POLL_SECONDS
        while str(file_info.get("state", "")).upper() == "PROCESSING":
            if time.time() > deadline:
                raise TimeoutError("Uploaded video did not finish processing in time")
            time.sleep(_POLL_INTERVAL_SECONDS)
            status_resp = requests_mod.get(
                f"{_BASE_URL}/{file_info.get('name')}",
                headers={"x-goog-api-key": api_key},
                timeout=15,
            )
            status_resp.raise_for_status()
            file_info = status_resp.json()
        if str(file_info.get("state", "")).upper() == "FAILED":
            raise RuntimeError("Files API failed to process the uploaded video")

        uri = file_info.get("uri")
        if not uri:
            raise RuntimeError(f"Files API response missing uri: {file_info}")
        return uri

    @staticmethod
    def _extract_output_video(data: dict[str, Any]) -> dict[str, Any] | None:
        """Find the output video payload ({'data': b64} or {'uri': files/...})."""
        for key in ("output_video", "outputVideo"):
            video = data.get(key)
            if isinstance(video, dict) and (video.get("data") or video.get("uri")):
                return video
        # REST responses may also carry the video inside steps[].content[].
        for step in data.get("steps") or []:
            for item in step.get("content") or []:
                if isinstance(item, dict) and (item.get("data") or item.get("uri")):
                    if "video" in str(item.get("type", "")).lower() or item.get("mime_type", "").startswith("video/"):

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Verify the source file plays locally (ffprobe/quick look) and is not truncated, then re-upload.
  2. Re-encode to H.264 MP4 with AAC audio — the most reliably supported format for the Files API.
  3. Check the file size matches expectation before passing it in; reject zero-byte files upstream.
  4. If the file is valid and small, inspect the file's error details via the Files API status response for a specific reason.
  5. Retry once — occasional transient transcode failures do occur.

Example fix

// before
{"video_path": "clip.mov"}  // ProRes 4444, Files API marks FAILED

// after (shell)
ffmpeg -i clip.mov -c:v libx264 -pix_fmt yuv420p -c:a aac clip.mp4
// then
{"video_path": "clip.mp4"}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
import subprocess

def validate_video_for_files_api(path: str) -> None:
    p = Path(path)
    if p.stat().st_size == 0:
        raise ValueError("empty file will be marked FAILED")
    subprocess.run(
        ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
         "stream=codec_name", "-of", "csv=p=0", str(p)],
        check=True, capture_output=True,
    )  # unreadable stream -> non-zero exit, don't upload

Try / catch

try:
    result = gemini_omni_video(inputs)
except RuntimeError as e:
    if "failed to process the uploaded video" in str(e).lower():
        inputs = {**inputs, "video_path": reencode_h264(inputs["video_path"])}
        result = gemini_omni_video(inputs)  # one retry with clean encoding
    else:
        raise

Prevention

When it happens

Trigger: After a video upload, the polling loop exits with file_info.state == 'FAILED' on the status GET to {BASE_URL}/{file.name}. Caused by corrupt/truncated uploads, unsupported codecs, or content rejected by Google's media pipeline.

Common situations: Truncated upload (network drop mid-PUT so the stored bytes are invalid); unsupported container/codec (e.g. some ProRes/HEVC variants); zero-byte file passed by mistake; policy-flagged content; partial disk write left an incomplete file.

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/ae44c7ce27a11165. Report an issue: GitHub.