{"record":{"id":"6fce05f34dc651b3","repo":"calesthio/OpenMontage","slug":"kling-video-response-contained-no-downloadable-url","errorCode":null,"errorMessage":"Kling video response contained no downloadable URL: {item}","messagePattern":"Kling video response contained no downloadable URL: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/video/kling_official_video.py","lineNumber":621,"sourceCode":"        base_path = Path(inputs.get(\"output_path\", \"kling_official_video.mp4\"))\n        paths: list[Path] = []\n        for index, item in enumerate(outputs):\n            url = self._output_url(item)\n            suffix = extension_from_url(url, \".mp4\")\n            output_path = numbered_output_path(output_path_with_suffix(base_path, suffix), index, suffix)\n            client.download(url, output_path)\n            paths.append(output_path)\n        return paths\n\n    @staticmethod\n    def _output_url(item: dict[str, Any]) -> str:\n        url = item.get(\"url\") or item.get(\"video_url\") or item.get(\"resource_url\")\n        if url:\n            return str(url)\n        resource = item.get(\"resource\") or {}\n        if isinstance(resource, dict) and resource.get(\"url\"):\n            return str(resource[\"url\"])\n        raise ValueError(f\"Kling video response contained no downloadable URL: {item}\")\n\n    @staticmethod\n    def _reference_metadata_from_classic_payload(payload: dict[str, Any]) -> list[dict[str, Any]]:\n        references: list[dict[str, Any]] = []\n        if payload.get(\"image\"):\n            references.append({\"kind\": \"image\", \"source_type\": \"reference_image\"})\n        if payload.get(\"image_tail\"):\n            references.append({\"kind\": \"image\", \"source_type\": \"reference_tail_image\"})\n        if payload.get(\"element_list\"):\n            references.extend(\n                {\"kind\": \"element\", \"element_id\": item[\"element_id\"]}\n                for item in normalize_element_list(payload.get(\"element_list\"))\n            )\n        return references\n\n    @staticmethod\n    def _callback_result_data(inputs: dict[str, Any], task_id: str) -> dict[str, Any]:\n        callback_url = inputs.get(\"callback_url\")","sourceCodeStart":603,"sourceCodeEnd":639,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/video/kling_official_video.py#L603-L639","documentation":"Raised by the static helper `_output_url(item)` when a single output object from the Kling response contains none of the known URL fields. The helper tries `url`, `video_url`, `resource_url` at the top level, then a nested `resource dict with a `url` key. If all are absent the item is un-downloadable and the raw item dict is embedded in the error so the developer can see exactly what shape came back.","triggerScenarios":"Kling returns an output item with a new/renamed field (e.g. `download_url`) not in the fallback chain; an item is a status/progress object accidentally mixed into the outputs list; the item contains only a task reference with the video still processing.","commonSituations":"Upstream API version drift adding new response shapes; polling code that feeds the whole task list rather than only completed works into `_download_videos`; content moderation or region flags that suppress the URL field without a clear error status.","solutions":["Print the `item` dict from the error message and identify which field actually carries the URL","If the shape is a legitimate new Kling field, extend `_output_url` with one more `item.get(...)` fallback in the chain","Filter outputs to only completed works before download, so in-progress/empty shells never reach this helper","Re-query the task once after a short delay if the item indicates processing rather than a final artifact"],"exampleFix":"// before\nurl = item.get(\"url\") or item.get(\"video_url\") or item.get(\"resource_url\")\n// after (extend for new provider field)\nurl = (\n    item.get(\"url\")\n    or item.get(\"video_url\")\n    or item.get(\"resource_url\")\n    or item.get(\"download_url\")\n)","handlingStrategy":"type-guard","validationCode":"KNOWN_URL_KEYS = (\"url\", \"video_url\", \"resource_url\")\ndef extract_output_url(item: dict) -> str | None:\n    for k in KNOWN_URL_KEYS:\n        if item.get(k):\n            return str(item[k])\n    res = item.get(\"resource\")\n    if isinstance(res, dict) and res.get(\"url\"):\n        return str(res[\"url\"])\n    return None","typeGuard":"def has_downloadable_url(item: dict) -> bool:\n    return bool(\n        item.get(\"url\") or item.get(\"video_url\") or item.get(\"resource_url\")\n        or (isinstance(item.get(\"resource\"), dict) and item[\"resource\"].get(\"url\"))\n    )","tryCatchPattern":"try:\n    url = tool._output_url(item)\nexcept ValueError:\n    logger.error(\"unrecognized output item shape: %s\", item)\n    raise","preventionTips":["Filter the outputs list with has_downloadable_url before calling download","Pin and monitor the Kling API version you build against; add regression fixtures with real response shapes","Log the raw item on failure — the error already embeds it, so capture it in your error tracker"],"tags":["kling","video-generation","response-parsing","api-drift"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}