calesthio/OpenMontage · error · RuntimeError

Files API response missing uri: {file_info}

Error message

Files API response missing uri: {file_info}

What it means

Raised when a Files API file record reaches a non-PROCESSING, non-FAILED state but contains no 'uri' field. After the poll loop, the tool unconditionally reads file_info['uri'] to build the reference for generation; a missing uri means the API returned an unexpected response shape and the tool cannot proceed safely, so it embeds the whole file_info dict in the error.

Source

Thrown at tools/video/gemini_omni_video.py:279

        # 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/"):
                        return item
                    if item.get("data") or str(item.get("uri", "")).startswith("files/"):
                        return item
        return None

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Retry the request — if the response body was a transient error page that parsed as JSON, a fresh call returns the normal shape.
  2. Print/log the full file_info from the error message to identify the actual state and response shape returned.
  3. Check for an OpenMontage update — if Google renamed/moved the uri field, a patched tool version is likely needed.
  4. Verify no corporate proxy or gateway is modifying API responses.
Defensive patterns

Strategy: retry

Try / catch

try:
    result = gemini_omni_video(inputs)
except RuntimeError as e:
    if "missing uri" in str(e):
        # response-shape drift or transient odd body: one retry, then surface
        result = gemini_omni_video(inputs)
    else:
        raise

Prevention

When it happens

Trigger: The status GET returns 200 with state neither PROCESSING nor FAILED (expected 'ACTIVE') yet the JSON body lacks a top-level 'uri' key — e.g. an API version change, an error payload shaped differently, or a new intermediate state that exits the loop.

Common situations: Google changes the Files API response schema (uri renamed or nested); a state like 'STATE_UNSPECIFIED' that falls through both checks; a proxy/gateway stripping response fields; relying on undocumented response shapes that shift without notice.

Related errors


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