{"record":{"id":"ff0c2594396f5862","repo":"calesthio/OpenMontage","slug":"unexpected-kling-classic-task-status-status-r","errorCode":null,"errorMessage":"Unexpected Kling Classic task status {status!r}","messagePattern":"Unexpected Kling Classic task status (.+?)","errorType":"exception","errorClass":"KlingAPIError","httpStatus":null,"severity":"error","filePath":"tools/_kling/client.py","lineNumber":99,"sourceCode":"        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(f\"{path.rstrip('/')}/{task_id}\")\n            payload = data.get(\"data\") or {}\n            status = payload.get(\"task_status\") or payload.get(\"status\")\n            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:","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/_kling/client.py#L81-L117","documentation":"Raised when poll_classic() receives a task_status value that matches neither the success, failure, nor known pending statuses (CLASSIC_PENDING_STATUSES). It is a defensive guard against Kling introducing new status enum values or returning an unexpected shape, and includes the full response payload so the unknown status can be inspected.","triggerScenarios":"Kling adds a new intermediate status (e.g. 'staging', 'queuing') not in CLASSIC_PENDING_STATUSES in schemas.py; the API returns an unusual status string for a task in an edge state (e.g. cancelled/expired tasks); payload.get('task_status') is empty and payload.get('status') carries an unexpected value.","commonSituations":"Kling API version drift after an upstream release; tasks that were cancelled manually in the Kling console and now report a cancel status; regional endpoints that emit slightly different status enums.","solutions":["Log the full response (attached as error.response) to see the exact status string","Check current Kling official API docs for the complete task_status enum","If the status is a new pending-like state, add it to CLASSIC_PENDING_STATUSES in tools/_kling/schemas.py","If it is a terminal state (cancelled/expired), add explicit handling for it instead of treating it as pending"],"exampleFix":"// schemas.py — before\nCLASSIC_PENDING_STATUSES = {'submitted', 'processing'}\n\n// schemas.py — after (after confirming against Kling docs)\nCLASSIC_PENDING_STATUSES = {'submitted', 'processing', 'queuing'}","handlingStrategy":"try-catch","validationCode":"KNOWN = {'succeed', 'failed', 'submitted', 'processing'}  # mirror schemas.py\n\ndef status_is_known(status: str) -> bool:\n    return status in KNOWN","typeGuard":"from tools._kling.schemas import CLASSIC_PENDING_STATUSES, CLASSIC_SUCCESS_STATUS, CLASSIC_FAILURE_STATUS\n\ndef classify_classic_status(payload: dict) -> str:\n    s = (payload.get('data') or {}).get('task_status') or (payload.get('data') or {}).get('status')\n    if s == CLASSIC_SUCCESS_STATUS: return 'success'\n    if s == CLASSIC_FAILURE_STATUS: return 'failure'\n    if s in CLASSIC_PENDING_STATUSES: return 'pending'\n    return 'unknown'","tryCatchPattern":"try:\n    outputs = client.poll_classic(path, task_id, 'videos')\nexcept KlingAPIError as e:\n    if 'Unexpected Kling Classic task status' in str(e):\n        logger.error('unknown status — full response: %s', e.response)\n        # decide: extend schemas.py or treat as failure\n    raise","preventionTips":["Pin/assert the API version you develop against","Monitor Kling release notes for new task_status enum values","Always log the attached response payload on unknown-status errors"],"tags":["kling","api","polling","schema-drift","unknown-status"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}