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.
Solutions
- Verify the source file plays locally (ffprobe/quick look) and is not truncated, then re-upload.
- Re-encode to H.264 MP4 with AAC audio — the most reliably supported format for the Files API.
- Check the file size matches expectation before passing it in; reject zero-byte files upstream.
- If the file is valid and small, inspect the file's error details via the Files API status response for a specific reason.
- 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
- Always upload H.264/AAC MP4 — most reliably accepted by the Files API.
- Never pass files whose size is 0 or differs from what your pipeline wrote.
- Treat one FAILED as a media problem first: fix the file, not the deadline.
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
- Uploaded video did not finish processing in time
- Files API did not return an upload URL
- Files API response missing uri
- Gemini Omni developer reference_to_video requires exactly…
- Gemini Omni video_edit accepts at most 10 reference images
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/ae44c7ce27a11165.
Report an issue: GitHub.
Appendix: 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)