calesthio/OpenMontage · error · RuntimeError

Kling result did not include a remote video URL for lip-sync

Error message

Kling result did not include a remote video URL for lip-sync input.

What it means

Raised by _first_remote_url when parsing a completed Kling video generation result. The helper looks for `remote_url` at the top level, then iterates `remote_outputs` for `url`/`video_url`/`resource_url` keys; if none is found it cannot build the input for the next pipeline stage (lip-sync needs a remote video URL) and raises. It almost always means the Kling API response schema changed or the result is structurally different from expected, not that generation failed.

Source

Thrown at scripts/kling_official_animated_explainer_e2e.py:470

        "estimated_cost_usd": sum(
            float(getattr(result, "cost_usd", 0) or 0)
            for result in (image_result, video_result)
        )
        + float(tts_data.get("estimated_cost_usd") or 0),
    }


def _first_remote_url(result_data: dict[str, Any]) -> str:
    direct = result_data.get("remote_url")
    if direct:
        return str(direct)
    for item in result_data.get("remote_outputs") or []:
        if not isinstance(item, dict):
            continue
        url = item.get("url") or item.get("video_url") or item.get("resource_url")
        if url:
            return str(url)
    raise RuntimeError("Kling result did not include a remote video URL for lip-sync input.")


def _run_live_avatar_suite(
    project_dir: Path,
    *,
    timeout_seconds: int,
    poll_interval: float,
) -> dict[str, Any]:
    image = registry.get("image_selector")
    avatar = registry.get("kling_avatar")
    lip_sync = registry.get("kling_lip_sync")
    assert image and avatar and lip_sync

    assets_dir = project_dir / "assets"
    image_dir = assets_dir / "images"
    video_dir = assets_dir / "video"
    artifacts_dir = project_dir / "artifacts"
    narration_path = assets_dir / "audio" / "narration.mp3"

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Print the full result_data dict and locate where the URL now lives (commonly under data.task_result.works[].resource_url in Kling responses)
  2. Update _first_remote_url to also check the actual field path found in the response, or pass the tool's documented output accessor instead of raw data
  3. Ensure the upstream result comes from a genuinely completed task (status succeeded), not a pending one polled early
  4. If only local files exist, upload/serve the artifact and construct the lip-sync input from a hosted URL

Example fix

# before
for item in result_data.get("remote_outputs") or []:
    url = item.get("url") or item.get("video_url") or item.get("resource_url")

# after (also accept Kling works[] shape)
for item in (result_data.get("remote_outputs")
             or ((result_data.get("data") or {}).get("task_result") or {}).get("works")
             or []):
    url = item.get("url") or item.get("video_url") or item.get("resource_url")
Defensive patterns

Strategy: type-guard

Validate before calling

def has_remote_url(result_data: dict) -> bool:
    if result_data.get("remote_url"):
        return True
    return any(
        isinstance(i, dict) and (i.get("url") or i.get("video_url") or i.get("resource_url"))
        for i in result_data.get("remote_outputs") or []
    )
if not has_remote_url(result_data):
    raise SystemExit("no remote URL in result; inspect payload before lip-sync")

Type guard

def extract_remote_url(result_data: dict) -> str | None:
    if result_data.get("remote_url"):
        return str(result_data["remote_url"])
    for item in result_data.get("remote_outputs") or []:
        if isinstance(item, dict):
            u = item.get("url") or item.get("video_url") or item.get("resource_url")
            if u:
                return str(u)
    return None

Try / catch

url = extract_remote_url(result_data)
if url is None:
    print(json.dumps(result_data, indent=2))  # schema drift diagnostics
    raise SystemExit("kling result schema lacks a remote video URL")

Prevention

When it happens

Trigger: Running the avatar smoke suite (--live-avatar or the avatar stage of --live-full): a kling_official_video/avatar call succeeds, then _first_remote_url(result.data) is called to feed the video URL into kling_lip_sync, but the response dict contains neither remote_url nor a usable remote_outputs entry.

Common situations: Kling API schema drift (renamed output fields); the avatar tool returning only local file paths (already downloaded artifacts) with no remote references; a partially-truncated result dict logged from an earlier session being replayed; empty remote_outputs list on an asynchronous task queried too early.

Related errors


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