{"record":{"id":"b73792d60890ef34","repo":"calesthio/OpenMontage","slug":"files-api-response-missing-uri-file-info","errorCode":null,"errorMessage":"Files API response missing uri: {file_info}","messagePattern":"Files API response missing uri: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/video/gemini_omni_video.py","lineNumber":279,"sourceCode":"        # Wait until the uploaded video is processed before referencing it.\n        deadline = time.time() + _MAX_POLL_SECONDS\n        while str(file_info.get(\"state\", \"\")).upper() == \"PROCESSING\":\n            if time.time() > deadline:\n                raise TimeoutError(\"Uploaded video did not finish processing in time\")\n            time.sleep(_POLL_INTERVAL_SECONDS)\n            status_resp = requests_mod.get(\n                f\"{_BASE_URL}/{file_info.get('name')}\",\n                headers={\"x-goog-api-key\": api_key},\n                timeout=15,\n            )\n            status_resp.raise_for_status()\n            file_info = status_resp.json()\n        if str(file_info.get(\"state\", \"\")).upper() == \"FAILED\":\n            raise RuntimeError(\"Files API failed to process the uploaded video\")\n\n        uri = file_info.get(\"uri\")\n        if not uri:\n            raise RuntimeError(f\"Files API response missing uri: {file_info}\")\n        return uri\n\n    @staticmethod\n    def _extract_output_video(data: dict[str, Any]) -> dict[str, Any] | None:\n        \"\"\"Find the output video payload ({'data': b64} or {'uri': files/...}).\"\"\"\n        for key in (\"output_video\", \"outputVideo\"):\n            video = data.get(key)\n            if isinstance(video, dict) and (video.get(\"data\") or video.get(\"uri\")):\n                return video\n        # REST responses may also carry the video inside steps[].content[].\n        for step in data.get(\"steps\") or []:\n            for item in step.get(\"content\") or []:\n                if isinstance(item, dict) and (item.get(\"data\") or item.get(\"uri\")):\n                    if \"video\" in str(item.get(\"type\", \"\")).lower() or item.get(\"mime_type\", \"\").startswith(\"video/\"):\n                        return item\n                    if item.get(\"data\") or str(item.get(\"uri\", \"\")).startswith(\"files/\"):\n                        return item\n        return None","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/video/gemini_omni_video.py#L261-L297","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the request — if the response body was a transient error page that parsed as JSON, a fresh call returns the normal shape.","Print/log the full file_info from the error message to identify the actual state and response shape returned.","Check for an OpenMontage update — if Google renamed/moved the uri field, a patched tool version is likely needed.","Verify no corporate proxy or gateway is modifying API responses."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    result = gemini_omni_video(inputs)\nexcept RuntimeError as e:\n    if \"missing uri\" in str(e):\n        # response-shape drift or transient odd body: one retry, then surface\n        result = gemini_omni_video(inputs)\n    else:\n        raise","preventionTips":["Pin to a known-good OpenMontage version so Files API schema drift is patched centrally.","Keep the full file_info from the exception — it is the only evidence of what the API returned.","Avoid network middleboxes that rewrite API response bodies."],"tags":["gemini","files-api","response-shape","api-contract","parsing"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}