calesthio/OpenMontage · error · KlingAPIError
Kling Classic result path data.task_result.{result_key} is n
Error message
Kling Classic result path data.task_result.{result_key} is not a list What it means
KlingAPIError raised inside poll_classic when the task reached the success status but data.task_result.<result_key> is present yet not a list. The poller's contract is to return a list of output records (usually one element with a URL); a dict, string, or null in that position violates the contract, so it refuses to return a malformed result rather than crash later on iteration.
Source
Thrown at tools/_kling/client.py:93
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 []
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,View on GitHub (pinned to 95e1c3d0ab)
Solutions
- Inspect the raw payload: temporarily log the full data at success status and find the actual key/shape under data.task_result
- Correct the result_key argument to the endpoint's documented result field for that route
- If the field legitimately became a single object, wrap it client-side: outputs = [obj] if isinstance(obj, dict) else obj
- Pin to a known-good provider API version / check the changelog if Kling shipped a breaking change
Example fix
# before
outputs = client.poll_classic(path, task_id, result_key="videos") # dict comes back
# after
outputs = client.poll_classic(path, task_id, result_key="task_result")
if isinstance(outputs, dict):
outputs = [outputs] Defensive patterns
Strategy: type-guard
Validate before calling
# verify result_key against the raw success payload once, then poll
sample = client.get(f"{path.rstrip('/')}/{task_id}")
result = ((sample.get("data") or {}).get("task_result") or {}).get(result_key)
if result is not None and not isinstance(result, list):
raise SystemExit(f"result_key {result_key!r} is {type(result).__name__}, not list — wrong key for this endpoint?") Type guard
def is_output_list(value) -> bool:
return isinstance(value, list) Try / catch
try:
outputs = client.poll_classic(path, task_id, result_key)
except KlingAPIError as e:
if "is not a list" in str(e):
raise SystemExit(f"result shape mismatch for key {result_key!r} — inspect data.task_result in the raw response")
raise Prevention
- Use the result_key documented for the exact endpoint, not one copied from another route's example
- Log the raw success payload once per endpoint integration to lock the schema
- Wrap single-object results into a list at the call boundary if an endpoint is object-shaped
When it happens
Trigger: Polling a completed classic task where result_key doesn't match this endpoint's actual result field — e.g. asking for 'task_result.images' when the route returns a single object under a different key — so .get(result_key) yields something non-list; also schema changes on Kling's side after success.
Common situations: Calling poll_classic with the result_key copied from a different endpoint's example; API revision changing an endpoint from list-shaped to object-shaped results; task_result partially populated for multi-output tasks.
Related errors
- Kling result did not include a remote video URL for lip-sync
- Kling Classic task failed
- Unexpected Kling Classic task status {status!r}
- Kling Classic task {task_id} timed out after {timeout_second
- Kling Turbo poll response missing data[0]: {data}
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/e5cb2d522b538e94.
Report an issue: GitHub.