calesthio/OpenMontage · error · KlingAPIError

Kling Classic create response missing data.task_id: {data}

Error message

Kling Classic create response missing data.task_id: {data}

What it means

KlingAPIError raised by create_classic_task when the POST succeeds (HTTP-level) but the response's data.task_id is missing/empty. The Kling 'classic' task-creation contract requires data.task_id as the handle for all subsequent polling; without it the task cannot be tracked. The entire response is embedded so you can see whether the server returned an error payload, a different schema, or a success shape without an id.

Source

Thrown at tools/_kling/client.py:73

    def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
        return self._request("get", path, params=params)

    def download(self, url: str, output_path: Path, timeout: int = 180) -> Path:
        output_path.parent.mkdir(parents=True, exist_ok=True)
        response = self.session.get(url, timeout=timeout)
        self._raise_for_http_error(response)
        content = getattr(response, "content", None)
        if content is None and hasattr(response, "iter_content"):
            content = b"".join(chunk for chunk in response.iter_content(chunk_size=1024 * 128) if chunk)
        output_path.write_bytes(content or b"")
        return output_path

    def create_classic_task(self, path: str, payload: dict[str, Any]) -> str:
        data = self.post(path, payload)
        task_id = ((data.get("data") or {}).get("task_id"))
        if not task_id:
            raise KlingAPIError(f"Kling Classic create response missing data.task_id: {data}")
        return str(task_id)

    def poll_classic(
        self,
        path: str,
        task_id: str,
        result_key: str,
        timeout_seconds: int = 900,
        poll_interval: float = 5.0,
    ) -> list[dict[str, Any]]:
        deadline = time.time() + timeout_seconds
        while time.time() < deadline:
            data = self.get(f"{path.rstrip('/')}/{task_id}")
            payload = data.get("data") or {}
            status = payload.get("task_status") or payload.get("status")
            if status == CLASSIC_SUCCESS_STATUS:
                task_result = payload.get("task_result") or {}
                outputs = task_result.get(result_key) or []

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Print the embedded response — if it contains code/message fields (e.g. 1002 invalid param, 3001 quota), fix that condition first
  2. Confirm the model key in the payload is one your account can call and matches the endpoint's expected naming
  3. Verify you're on the right endpoint family: create_turbo() expects data.id, create_classic_task() expects data.task_id — don't mix them
  4. Check KLING_API_BASE_URL points at the correct official base if overridden

Example fix

# before
task_id = client.create_classic_task("/v1/videos/text2video", payload)

# after (surface the raw body to see why no id came back)
data = client.post("/v1/videos/text2video", payload)
print(data)  # inspect code/message, then fix payload or account access
task_id = client.create_classic_task("/v1/videos/text2video", payload)
Defensive patterns

Strategy: type-guard

Validate before calling

resp = client.post(path, payload)
task_id = (resp.get("data") or {}).get("task_id")
if not task_id:
    # inspect body for a business error before retrying anything
    raise SystemExit(f"no task_id in create response: {resp.get('code')}/{resp.get('message')}")

Type guard

def has_classic_task_id(data: dict) -> bool:
    return bool((data.get("data") or {}).get("task_id"))

Try / catch

try:
    task_id = client.create_classic_task(path, payload)
except KlingAPIError as e:
    body = str(e)
    if "missing data.task_id" in body:
        # body embeds the full response — check code/message for quota/param errors
        raise SystemExit(f"create rejected: {body}")
    raise

Prevention

When it happens

Trigger: Calling create_classic_task for image/video endpoints (e.g. /v1/images/generations, text-to-video classic routes) when the account lacks permission for the model, a param was invalid and Kling returned an error object instead of raising HTTP error status, or an API revision moved the id field (some routes use data.id for 'turbo' style).

Common situations: Free-tier key hitting an enterprise-only model (200-with-error-body responses are common on Chinese API gateways); endpoint path typo producing a different JSON shape; mixing classic vs turbo route conventions; base URL override pointing at a different regional deployment.

Related errors


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