calesthio/OpenMontage · error · ValueError
Kling image response item contained no downloadable URL: {it
Error message
Kling image response item contained no downloadable URL: {item} What it means
Raised by KlingOfficialImage._output_url when a completed Kling image task returns a results item that contains none of the URL fields the tool knows how to download ('url', 'image_url', 'resource_url', or a nested 'resource.url'). It means the task reached a terminal state but the response shape does not match any expected layout, so there is nothing to fetch and write to disk. This is almost always an API-side schema change or an unusual response variant rather than a caller mistake.
Source
Thrown at tools/graphics/kling_official_image.py:390
base_path = Path(inputs.get("output_path", "kling_official_image.png"))
paths: list[Path] = []
for index, item in enumerate(outputs):
url = self._output_url(item)
suffix = extension_from_url(url, ".png")
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("image_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 image response item contained no downloadable URL: {item}")
@staticmethod
def _prompt(inputs: dict[str, Any]) -> str:
prompt = str(inputs.get("prompt") or "").strip()
if not prompt:
raise ValueError("prompt is required")
return prompt
@staticmethod
def _copy_common_task_fields(inputs: dict[str, Any], payload: dict[str, Any]) -> None:
if "watermark" in inputs:
payload["watermark_info"] = {"enabled": bool(inputs.get("watermark"))}
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"]
View on GitHub (pinned to 95e1c3d0ab)
Solutions
- Print/inspect the full item dict from the exception message to see which field actually carries the URL
- Add the observed key to the _output_url candidate chain (url/image_url/resource_url/resource.url)
- Check the Kling API changelog for the model used and update the tool
- If items contain base64 data instead of URLs, decode and write directly instead of downloading
Example fix
// before
url = item.get("url") or item.get("image_url") or item.get("resource_url")
// after
url = (
item.get("url")
or item.get("image_url")
or item.get("resource_url")
or (item.get("resource") or {}).get("url")
or item.get("download_url") # newly observed field
) Defensive patterns
Strategy: try-catch
Validate before calling
def has_downloadable_url(item: dict) -> bool:
if item.get("url") or item.get("image_url") or item.get("resource_url"):
return True
res = item.get("resource")
return isinstance(res, dict) and bool(res.get("url")) Type guard
def is_kling_result_item(item: Any) -> bool:
return isinstance(item, dict) and bool(
item.get("url") or item.get("image_url")
or item.get("resource_url")
or (isinstance(item.get("resource"), dict) and item["resource"].get("url"))
) Try / catch
try:
paths = tool.run(inputs)
except ValueError as e:
if "no downloadable URL" in str(e):
logger.error("Kling response schema drift; item=%s", e)
# surface item payload for schema fix; do not retry unchanged
raise Prevention
- Pin to tested Kling API/model versions
- Log full response items in debug mode so schema drift is diagnosable
- Wrap provider calls with a response-shape assertion layer that alerts on drift
When it happens
Trigger: Calling kling_official_image with a prompt that succeeds at task level; the polled task detail returns items in results whose entries only carry, e.g., a base64 field or a differently-named key. Also triggered if the API starts wrapping URLs one level deeper than 'resource'.
Common situations: Kling ships a new model variant whose response payload differs; a proxy/gateway rewrites the response; the tool's supported field list falls behind the live API version.
Related errors
- Kling result did not include a remote video URL for lip-sync
- Kling Classic result path data.task_result.{result_key} is n
- model_name {model_name!r} is not supported for api_family=ge
- prompt exceeds Kling image generation limit of 2500 characte
- model_name {model_name!r} is not supported for api_family=om
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/470e9dcbabbe5fae.
Report an issue: GitHub.