calesthio/OpenMontage · error · RuntimeError

Files API did not return an upload URL

Error message

Files API did not return an upload URL

What it means

Raised as RuntimeError when the Google Files API resumable-upload start request succeeds (2xx) but the response lacks the X-Goog-Upload-URL header needed for the subsequent upload+finalize POST. Without that header the protocol cannot proceed, so the tool aborts before sending video bytes.

Source

Thrown at tools/video/gemini_omni_video.py:246

        video_bytes = path.read_bytes()

        start_resp = requests_mod.post(
            _UPLOAD_URL,
            headers={
                "x-goog-api-key": api_key,
                "X-Goog-Upload-Protocol": "resumable",
                "X-Goog-Upload-Command": "start",
                "X-Goog-Upload-Header-Content-Length": str(len(video_bytes)),
                "X-Goog-Upload-Header-Content-Type": mime_type,
                "Content-Type": "application/json",
            },
            json={"file": {"display_name": path.name}},
            timeout=30,
        )
        start_resp.raise_for_status()
        upload_url = start_resp.headers.get("X-Goog-Upload-URL")
        if not upload_url:
            raise RuntimeError("Files API did not return an upload URL")

        upload_resp = requests_mod.post(
            upload_url,
            headers={
                "X-Goog-Upload-Command": "upload, finalize",
                "X-Goog-Upload-Offset": "0",
                "Content-Length": str(len(video_bytes)),
            },
            data=video_bytes,
            timeout=300,
        )
        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:

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Retry the operation — transient start-session failures usually resolve immediately
  2. If behind a proxy, ensure it forwards the X-Goog-* response headers, or bypass it for googleapis.com
  3. Inspect start_resp.headers (and body) in a debug run to see whether the URL moved to the JSON body; if so the tool needs updating to read it
  4. Check API key validity and Files API quota — some rejection paths can surface here

Example fix

# before (single attempt)
result = gemini_omni_video.run(inputs={'prompt':'...','video_path':vid})

# after (bounded retry)
for attempt in range(3):
    try:
        result = gemini_omni_video.run(inputs={'prompt':'...','video_path':vid}); break
    except RuntimeError as e:
        if 'upload URL' not in str(e) or attempt == 2: raise
        time.sleep(2 ** attempt)
Defensive patterns

Strategy: retry

Validate before calling

# nothing caller-side can validate — but you can preflight reachability/headers
import requests
r = requests.post('https://generativelanguage.googleapis.com/upload/v1beta/files',
                  headers={'x-goog-api-key': api_key}, timeout=10)
# if your proxy strips X-Goog-* headers, this surfaces it before the real run

Try / catch

for attempt in range(3):
    try:
        result = gemini_omni_video.run(inputs=inputs)
        break
    except RuntimeError as e:
        if 'upload URL' not in str(e) or attempt == 2:
            raise
        time.sleep(2 ** attempt)

Prevention

When it happens

Trigger: Transient API behavior or a proxy/CDN stripping the X-Goog-Upload-URL response header; an API version change where the Files API returns the URL in the body instead of the header; auth quota states that alter the start response shape.

Common situations: Corporate proxies (mitm/nginx) filtering unknown X- headers in both directions; intermittent Google-side issues where the start call 200s without a session; SDK/endpoint drift after Google changes the upload protocol.

Related errors


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