{"record":{"id":"7504cab68acc18db","repo":"calesthio/OpenMontage","slug":"kling-tts-response-item-contained-no-downloadable","errorCode":null,"errorMessage":"Kling TTS response item contained no downloadable URL: {item}","messagePattern":"Kling TTS response item contained no downloadable URL: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/audio/kling_tts.py","lineNumber":302,"sourceCode":"        base_path = Path(inputs.get(\"output_path\", \"kling_tts.mp3\"))\n        paths: list[Path] = []\n        for index, item in enumerate(outputs):\n            url = self._output_url(item)\n            suffix = extension_from_url(url, \".mp3\")\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(\"audio_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 TTS 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":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/audio/kling_tts.py#L284-L320","documentation":"Raised as ValueError by the static _output_url helper when a single audios entry has none of the known URL keys: url, audio_url, resource_url, or resource.url. It is a per-item shape check with the entire item embedded in the message, so the offending object is visible in the error.","triggerScenarios":"Kling changes the per-audio object schema (new key like 'file_url'); item contains only a task-relative path or an id requiring a second fetch; gateway strips URL fields.","commonSituations":"API version drift adding/renameing URL fields; items that reference a resource endpoint instead of a direct URL; test fixtures built from outdated docs.","solutions":["Inspect the embedded item in the error message to find the new key name","Extend _output_url's key chain (url → audio_url → resource_url → resource.url) with the new key","If the item holds an id/path, add a follow-up fetch to resolve it to a URL"],"exampleFix":"// before\nurl = item.get(\"url\") or item.get(\"audio_url\") or item.get(\"resource_url\")\n\n// after\nurl = (\n    item.get(\"url\")\n    or item.get(\"audio_url\")\n    or item.get(\"resource_url\")\n    or item.get(\"file_url\")  # new upstream key\n)","handlingStrategy":"validation","validationCode":"def item_has_url(item: dict) -> bool:\n    return bool(\n        item.get(\"url\") or item.get(\"audio_url\") or item.get(\"resource_url\")\n        or (isinstance(item.get(\"resource\"), dict) and item[\"resource\"].get(\"url\"))\n    )","typeGuard":"def is_downloadable_item(item: Any) -> bool:\n    if not isinstance(item, dict):\n        return False\n    resource = item.get(\"resource\")\n    return bool(\n        item.get(\"url\") or item.get(\"audio_url\") or item.get(\"resource_url\")\n        or (isinstance(resource, dict) and resource.get(\"url\"))\n    )","tryCatchPattern":"try:\n    paths = kling_tts_tool._download_audios(client, outputs, inputs)\nexcept ValueError as e:\n    if \"no downloadable URL\" in str(e):\n        logger.error(\"kling audio item schema changed: %s\", e)\n    raise","preventionTips":["The full offending item is embedded in the message — read it before changing code","Extend the URL-key chain when upstream adds fields, keeping old keys for compat","Keep test fixtures sourced from captured real responses"],"tags":["kling","tts","api-contract","download"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}