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
- Retry the operation — transient start-session failures usually resolve immediately
- If behind a proxy, ensure it forwards the X-Goog-* response headers, or bypass it for googleapis.com
- 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
- 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
- Retry transient upload-start failures with exponential backoff (2-3 attempts)
- Ensure proxies pass X-Goog-* request and response headers through untouched
- Keep the tool updated — if Google moves the URL into the body, the parser needs the change
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
- Uploaded video did not finish processing in time
- Uploading {path.name} to Atlas Cloud failed: {exc}
- Files API failed to process the uploaded video
- Files API response missing uri: {file_info}
- DashScope ASR task {task_id} did not finish within {timeout_
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/e42c317cbb851129.
Report an issue: GitHub.