calesthio/OpenMontage · error · ValueError

Kling avatar response item contained no downloadable URL: {i

Error message

Kling avatar response item contained no downloadable URL: {item}

What it means

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.

Source

Thrown at tools/avatar/kling_avatar.py:304

        base_path = Path(inputs.get("output_path", "kling_avatar.mp4"))
        paths: list[Path] = []
        for index, item in enumerate(outputs):
            url = self._output_url(item)
            suffix = extension_from_url(url, ".mp4")
            output_path = numbered_output_path(output_path_with_suffix(base_path, suffix), index, suffix)
            client.download(url, output_path)
            paths.append(output_path)
        return paths

    @staticmethod
    def _output_url(item: dict[str, Any]) -> str:
        url = item.get("url") or item.get("video_url") or item.get("resource_url")
        if url:
            return str(url)
        resource = item.get("resource") or {}
        if isinstance(resource, dict) and resource.get("url"):
            return str(resource["url"])
        raise ValueError(f"Kling avatar response item contained no downloadable URL: {item}")

    @staticmethod
    def _copy_common_task_fields(inputs: dict[str, Any], payload: dict[str, Any]) -> None:
        callback_url = validate_callback_url(inputs.get("callback_url"))
        if callback_url:
            payload["callback_url"] = callback_url
        if inputs.get("external_task_id"):
            payload["external_task_id"] = inputs["external_task_id"]

    @staticmethod
    def _callback_result_data(inputs: dict[str, Any], task_id: str) -> dict[str, Any]:
        callback_url = inputs.get("callback_url")
        if not callback_url:
            return {}
        return {
            "callback_url": str(callback_url),
            "callback_requested": True,
            "polling_used": True,

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Read the item JSON in the error message — it shows exactly which keys the entry has.
  2. Add the actual key to the fallback chain in _output_url.
  3. 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.

Example fix

// before
url = item.get("url") or item.get("video_url") or item.get("resource_url")

// after
url = (
    item.get("url")
    or item.get("video_url")
    or item.get("resource_url")
    or item.get("video_result", {}).get("url")
)
Defensive patterns

Strategy: type-guard

Validate before calling

def output_url(item: dict) -> str | None:
    url = item.get("url") or item.get("video_url") or item.get("resource_url")
    if not url:
        resource = item.get("resource") or {}
        url = resource.get("url") if isinstance(resource, dict) else None
    return str(url) if url else None

Type guard

def has_downloadable_url(item: dict) -> bool:
    return output_url(item) is not None

Try / catch

urls = [output_url(i) for i in outputs]
if any(u is None for u in urls):
    log.warning("schema drift in avatar outputs: %s", outputs)

Prevention

When it happens

Trigger: 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.

Common situations: Gateway firmware/API version changing the response field names; mixing responses from different Kling endpoints through the same downloader.

Related errors


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