{"record":{"id":"4afc2503baf0fac4","repo":"calesthio/OpenMontage","slug":"kling-turbo-create-response-missing-data-id-data","errorCode":null,"errorMessage":"Kling Turbo create response missing data.id: {data}","messagePattern":"Kling Turbo create response missing data\\.id: (.+?)","errorType":"exception","errorClass":"KlingAPIError","httpStatus":null,"severity":"error","filePath":"tools/_kling/client.py","lineNumber":107,"sourceCode":"            if status == CLASSIC_SUCCESS_STATUS:\n                task_result = payload.get(\"task_result\") or {}\n                outputs = task_result.get(result_key) or []\n                if not isinstance(outputs, list):\n                    raise KlingAPIError(f\"Kling Classic result path data.task_result.{result_key} is not a list\")\n                return outputs\n            if status == CLASSIC_FAILURE_STATUS:\n                message = payload.get(\"task_status_msg\") or payload.get(\"message\") or \"Kling Classic task failed\"\n                raise KlingAPIError(str(message), code=payload.get(\"task_status\"), response=data)\n            if status not in CLASSIC_PENDING_STATUSES:\n                raise KlingAPIError(f\"Unexpected Kling Classic task status {status!r}\", response=data)\n            time.sleep(min(poll_interval, max(0.0, deadline - time.time())))\n        raise TimeoutError(f\"Kling Classic task {task_id} timed out after {timeout_seconds}s\")\n\n    def create_turbo(self, path: str, payload: dict[str, Any]) -> str:\n        data = self.post(path, payload)\n        task_id = ((data.get(\"data\") or {}).get(\"id\"))\n        if not task_id:\n            raise KlingAPIError(f\"Kling Turbo create response missing data.id: {data}\")\n        return str(task_id)\n\n    def poll_turbo(\n        self,\n        task_id: str,\n        timeout_seconds: int = 900,\n        poll_interval: float = 5.0,\n    ) -> list[dict[str, Any]]:\n        deadline = time.time() + timeout_seconds\n        while time.time() < deadline:\n            data = self.get(\"/tasks\", params={\"task_ids\": task_id})\n            records = data.get(\"data\") or []\n            if not records:\n                raise KlingAPIError(f\"Kling Turbo poll response missing data[0]: {data}\")\n            record = records[0]\n            status = record.get(\"status\") or record.get(\"task_status\")\n            if status == TURBO_SUCCESS_STATUS:\n                outputs = record.get(\"outputs\") or []","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/_kling/client.py#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Verify the path argument matches the Turbo gateway's documented create endpoint","Check KLING_API_BASE_URL / base_url points at the correct Turbo deployment","If the gateway now returns data.task_id, normalize the parser to accept both keys"],"exampleFix":"// before\ntask_id = client.create_turbo('/v2/videos', payload)\n\n// after (inspect response, then adjust key if gateway changed)\nresp = client.post('/v2/videos', payload)\ntask_id = (resp.get('data') or {}).get('id') or (resp.get('data') or {}).get('task_id')\nif not task_id:\n    raise RuntimeError(f'unexpected create response: {resp}')","handlingStrategy":"try-catch","validationCode":"def turbo_create_response_has_id(data: dict) -> bool:\n    return bool((data.get('data') or {}).get('id'))","typeGuard":"def extract_turbo_task_id(data: dict) -> str | None:\n    d = data.get('data')\n    if isinstance(d, dict):\n        tid = d.get('id') or d.get('task_id')\n        if tid:\n            return str(tid)\n    return None","tryCatchPattern":"try:\n    task_id = client.create_turbo(path, payload)\nexcept KlingAPIError as e:\n    logger.error('turbo create rejected, body: %s', e.response)\n    raise","preventionTips":["Smoke-test the create path once after any gateway/base_url change","Keep business-error detection in _raise_for_business_error aligned with the gateway's error envelope","Log the full body — it usually contains the soft error the gateway returned"],"tags":["kling","api","turbo","response-shape","task-creation"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}