calesthio/OpenMontage · error · ValueError

Kling video response contained no downloadable URL: {item}

Error message

Kling video response contained no downloadable URL: {item}

What it means

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.

Source

Thrown at tools/video/kling_official_video.py:621

        base_path = Path(inputs.get("output_path", "kling_official_video.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 video response contained no downloadable URL: {item}")

    @staticmethod
    def _reference_metadata_from_classic_payload(payload: dict[str, Any]) -> list[dict[str, Any]]:
        references: list[dict[str, Any]] = []
        if payload.get("image"):
            references.append({"kind": "image", "source_type": "reference_image"})
        if payload.get("image_tail"):
            references.append({"kind": "image", "source_type": "reference_tail_image"})
        if payload.get("element_list"):
            references.extend(
                {"kind": "element", "element_id": item["element_id"]}
                for item in normalize_element_list(payload.get("element_list"))
            )
        return references

    @staticmethod
    def _callback_result_data(inputs: dict[str, Any], task_id: str) -> dict[str, Any]:
        callback_url = inputs.get("callback_url")

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Print the `item` dict from the error message and identify which field actually carries the URL
  2. If the shape is a legitimate new Kling field, extend `_output_url` with one more `item.get(...)` fallback in the chain
  3. Filter outputs to only completed works before download, so in-progress/empty shells never reach this helper
  4. Re-query the task once after a short delay if the item indicates processing rather than a final artifact

Example fix

// before
url = item.get("url") or item.get("video_url") or item.get("resource_url")
// after (extend for new provider field)
url = (
    item.get("url")
    or item.get("video_url")
    or item.get("resource_url")
    or item.get("download_url")
)
Defensive patterns

Strategy: type-guard

Validate before calling

KNOWN_URL_KEYS = ("url", "video_url", "resource_url")
def extract_output_url(item: dict) -> str | None:
    for k in KNOWN_URL_KEYS:
        if item.get(k):
            return str(item[k])
    res = item.get("resource")
    if isinstance(res, dict) and res.get("url"):
        return str(res["url"])
    return None

Type guard

def has_downloadable_url(item: dict) -> bool:
    return bool(
        item.get("url") or item.get("video_url") or item.get("resource_url")
        or (isinstance(item.get("resource"), dict) and item["resource"].get("url"))
    )

Try / catch

try:
    url = tool._output_url(item)
except ValueError:
    logger.error("unrecognized output item shape: %s", item)
    raise

Prevention

When it happens

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

Common situations: 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.

Related errors


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