{"record":{"id":"9aa826258475194c","repo":"calesthio/OpenMontage","slug":"kling-avatar-response-item-contained-no-downloadab","errorCode":null,"errorMessage":"Kling avatar response item contained no downloadable URL: {item}","messagePattern":"Kling avatar response item contained no downloadable URL: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/avatar/kling_avatar.py","lineNumber":304,"sourceCode":"        base_path = Path(inputs.get(\"output_path\", \"kling_avatar.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 avatar response item contained no downloadable URL: {item}\")\n\n    @staticmethod\n    def _copy_common_task_fields(inputs: dict[str, Any], payload: dict[str, Any]) -> None:\n        callback_url = validate_callback_url(inputs.get(\"callback_url\"))\n        if callback_url:\n            payload[\"callback_url\"] = callback_url\n        if inputs.get(\"external_task_id\"):\n            payload[\"external_task_id\"] = inputs[\"external_task_id\"]\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\")\n        if not callback_url:\n            return {}\n        return {\n            \"callback_url\": str(callback_url),\n            \"callback_requested\": True,\n            \"polling_used\": True,","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/avatar/kling_avatar.py#L286-L322","documentation":"ValueError from KlingAvatarTool._output_url when a single result item carries no downloadable URL. The helper tries url, video_url, resource_url, then resource.url; if all are absent the item is undownloadable and the whole item (with its JSON) is embedded in the error for diagnosis. It indicates schema drift or a degenerate output entry rather than a user mistake.","triggerScenarios":"Kling returns video entries where the URL sits under a new key (e.g. video_result.url or data.url) not covered by the fallback chain; an entry is a status/metadata object rather than a video object.","commonSituations":"Gateway firmware/API version changing the response field names; mixing responses from different Kling endpoints through the same downloader.","solutions":["Read the item JSON in the error message — it shows exactly which keys the entry has.","Add the actual key to the fallback chain in _output_url.","If the entry is not a video object (no URL of any kind), filter such entries out before download and check the task's status fields for the real failure."],"exampleFix":"// before\nurl = item.get(\"url\") or item.get(\"video_url\") or item.get(\"resource_url\")\n\n// after\nurl = (\n    item.get(\"url\")\n    or item.get(\"video_url\")\n    or item.get(\"resource_url\")\n    or item.get(\"video_result\", {}).get(\"url\")\n)","handlingStrategy":"type-guard","validationCode":"def output_url(item: dict) -> str | None:\n    url = item.get(\"url\") or item.get(\"video_url\") or item.get(\"resource_url\")\n    if not url:\n        resource = item.get(\"resource\") or {}\n        url = resource.get(\"url\") if isinstance(resource, dict) else None\n    return str(url) if url else None","typeGuard":"def has_downloadable_url(item: dict) -> bool:\n    return output_url(item) is not None","tryCatchPattern":"urls = [output_url(i) for i in outputs]\nif any(u is None for u in urls):\n    log.warning(\"schema drift in avatar outputs: %s\", outputs)","preventionTips":["Filter result items through a URL-extraction helper before download","Log unrecognized item shapes to catch gateway schema drift on day one"],"tags":["kling","avatar","schema-mismatch","download"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}