calesthio/OpenMontage · error · KlingAPIError

Kling Turbo create response missing data.id: {data}

Error message

Kling Turbo create response missing data.id: {data}

What it means

Raised when create_turbo() posts successfully (HTTP and business-error checks passed) but the response JSON has no data.id field. The client expects the Turbo task-creation response shape {data: {id: ...}} and refuses to proceed without it; the full response body is embedded in the message for diagnosis.

Source

Thrown at tools/_kling/client.py:107

            if status == CLASSIC_SUCCESS_STATUS:
                task_result = payload.get("task_result") or {}
                outputs = task_result.get(result_key) or []
                if not isinstance(outputs, list):
                    raise KlingAPIError(f"Kling Classic result path data.task_result.{result_key} is not a list")
                return outputs
            if status == CLASSIC_FAILURE_STATUS:
                message = payload.get("task_status_msg") or payload.get("message") or "Kling Classic task failed"
                raise KlingAPIError(str(message), code=payload.get("task_status"), response=data)
            if status not in CLASSIC_PENDING_STATUSES:
                raise KlingAPIError(f"Unexpected Kling Classic task status {status!r}", response=data)
            time.sleep(min(poll_interval, max(0.0, deadline - time.time())))
        raise TimeoutError(f"Kling Classic task {task_id} timed out after {timeout_seconds}s")

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

    def poll_turbo(
        self,
        task_id: 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("/tasks", params={"task_ids": task_id})
            records = data.get("data") or []
            if not records:
                raise KlingAPIError(f"Kling Turbo poll response missing data[0]: {data}")
            record = records[0]
            status = record.get("status") or record.get("task_status")
            if status == TURBO_SUCCESS_STATUS:
                outputs = record.get("outputs") or []

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Read the embedded response JSON in the message — if it contains an error/message field, the gateway returned a soft error; address that root cause
  2. Verify the path argument matches the Turbo gateway's documented create endpoint
  3. Check KLING_API_BASE_URL / base_url points at the correct Turbo deployment
  4. If the gateway now returns data.task_id, normalize the parser to accept both keys

Example fix

// before
task_id = client.create_turbo('/v2/videos', payload)

// after (inspect response, then adjust key if gateway changed)
resp = client.post('/v2/videos', payload)
task_id = (resp.get('data') or {}).get('id') or (resp.get('data') or {}).get('task_id')
if not task_id:
    raise RuntimeError(f'unexpected create response: {resp}')
Defensive patterns

Strategy: try-catch

Validate before calling

def turbo_create_response_has_id(data: dict) -> bool:
    return bool((data.get('data') or {}).get('id'))

Type guard

def extract_turbo_task_id(data: dict) -> str | None:
    d = data.get('data')
    if isinstance(d, dict):
        tid = d.get('id') or d.get('task_id')
        if tid:
            return str(tid)
    return None

Try / catch

try:
    task_id = client.create_turbo(path, payload)
except KlingAPIError as e:
    logger.error('turbo create rejected, body: %s', e.response)
    raise

Prevention

When it happens

Trigger: Calling create_turbo() with a wrong path for the deployed Turbo gateway; the Turbo endpoint returning a differently-shaped success payload (e.g. data.task_id instead of data.id); a gateway/proxy (HeyGen-style Turbo proxy) that swallows errors into a 200 response with an error object instead of a data object.

Common situations: Base URL misconfiguration via KLING_API_BASE_URL pointing at a different Turbo deployment; API contract change on the Turbo gateway; passing a payload the gateway rejects softly (200 + error body) instead of with an HTTP error status.

Related errors


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