calesthio/OpenMontage · error · KlingAPIError
Kling Turbo result path data[0].outputs is not a list
Error message
Kling Turbo result path data[0].outputs is not a list
What it means
Defensive type check in poll_turbo(): the task reached TURBO_SUCCESS_STATUS but record['outputs'] is not a list (it is None-coerced to [] only if falsy; a non-empty dict or string triggers the error). This indicates the Turbo gateway's success payload no longer matches the expected data[0].outputs array shape.
Source
Thrown at tools/_kling/client.py:127
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 []
if not isinstance(outputs, list):
raise KlingAPIError("Kling Turbo result path data[0].outputs is not a list")
return outputs
if status == TURBO_FAILURE_STATUS:
message = record.get("message") or record.get("error") or "Kling Turbo task failed"
raise KlingAPIError(str(message), code=record.get("code"), request_id=record.get("request_id"), response=data)
if status not in TURBO_PENDING_STATUSES:
raise KlingAPIError(f"Unexpected Kling Turbo task status {status!r}", response=data)
time.sleep(min(poll_interval, max(0.0, deadline - time.time())))
raise TimeoutError(f"Kling Turbo task {task_id} timed out after {timeout_seconds}s")
def _request(self, method: str, path: str, **kwargs: Any) -> dict[str, Any]:
url = self._url(path)
last_error: KlingAPIError | None = None
for attempt in range(self.max_retries + 1):
try:
response = getattr(self.session, method)(url, headers=self.headers, timeout=30, **kwargs)
self._raise_for_http_error(response)
data = response.json()
self._raise_for_business_error(data)View on GitHub (pinned to 95e1c3d0ab)
Solutions
- Log the full record (in error.response) to see the actual type of outputs
- Update poll_turbo() to handle the new shape (e.g. dict of lists, or string URL)
- Pin the gateway/API version if the Turbo deployment offers versioned paths
- Report/confirm the contract change against current Turbo API docs
Example fix
// before
outputs = record.get('outputs') or []
if not isinstance(outputs, list):
raise KlingAPIError('Kling Turbo result path data[0].outputs is not a list')
// after (handle dict-of-lists shape)
outputs = record.get('outputs') or []
if isinstance(outputs, dict):
outputs = [item for items in outputs.values() if isinstance(items, list) for item in items]
elif not isinstance(outputs, list):
raise KlingAPIError(f'unexpected outputs shape: {type(outputs).__name__}') Defensive patterns
Strategy: type-guard
Validate before calling
def outputs_is_list(record: dict) -> bool:
return isinstance(record.get('outputs'), list) Type guard
def extract_turbo_outputs(record: dict) -> list:
outputs = record.get('outputs')
if isinstance(outputs, list):
return outputs
if isinstance(outputs, dict): # shape drift: dict of lists
return [i for v in outputs.values() if isinstance(v, list) for i in v]
raise KlingAPIError(f'unexpected outputs type: {type(outputs).__name__}') Try / catch
try:
outputs = client.poll_turbo(task_id)
except KlingAPIError as e:
if 'outputs is not a list' in str(e):
logger.error('schema drift, record: %s', e.response)
raise Prevention
- Contract-test poll parsing against a recorded success payload in CI
- Log the raw record whenever the outputs shape is unexpected
- Pin gateway/API versions where available
When it happens
Trigger: The Turbo API changes its success schema, e.g. outputs becomes an object keyed by content type ({'videos': [...], 'images': [...]}) or a URL string; a proxy wraps outputs in an extra envelope; a partial/A-B response shape reaches the client.
Common situations: Upstream Turbo API version bump changing the result contract; a custom gateway aggregator (fal/HeyGen-style) normalizing outputs differently than the raw Kling shape.
Related errors
- Kling Turbo create response missing data.id: {data}
- Unexpected Kling Turbo task status {status!r}
- Unexpected Kling Classic task status {status!r}
- Kling Turbo poll response missing data[0]: {data}
- Kling Turbo task failed
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/02414222d583f131.
Report an issue: GitHub.