sgl-project/sglang · error · ValueError
{kind} transition actions must be a list
Error message
{kind} transition actions must be a list What it means
Raised while parsing a control-state event payload: each transition in the event's transitions list must itself carry an `actions` key whose value is a JSON list. The parser (called from parse_control_event_payload) refuses anything else so downstream normalize_state_payload always receives a list.
Source
Thrown at python/sglang/multimodal_gen/runtime/realtime/control_signals.py:127
def _control_state_transitions_from_event_payload(
payload: dict[str, Any],
*,
event_id: int | None,
kind: str,
normalize_state_payload: ControlStatePayloadNormalizer,
) -> list[ControlStateTransition]:
transitions = payload.get("transitions")
if not isinstance(transitions, list):
raise ValueError(f"{kind} state payload requires transitions")
result = []
for transition in transitions:
if not isinstance(transition, dict):
raise ValueError(f"{kind} transition must be a map")
actions = transition.get("actions")
if not isinstance(actions, list):
raise ValueError(f"{kind} transition actions must be a list")
timestamp_ms = transition.get("client_ts_ms")
if timestamp_ms is not None:
timestamp_ms = int(timestamp_ms)
result.append(
ControlStateTransition(
payload=normalize_state_payload(actions),
seq_id=event_id,
timestamp_ms=timestamp_ms,
)
)
return result
class ControlSignalQueue:
"""FIFO storage for discrete realtime control signals
Script-mode controls and one-shot signals are already expressed as discrete
payloads, so sampling only consumes queued signals and applies the requestedView on GitHub (pinned to 0132848349)
Solutions
- Fix the producer of the control event so each transition's actions is a JSON array (e.g. [{"type": ...}, ...]).
- If actions legitimately arrives as an object, coerce it to a list before calling parse_control_event_payload.
- Add a schema check (jsonschema/pydantic) at the event boundary so malformed events fail loudly with a clearer message.
Example fix
// before
{"transitions": [{"client_ts_ms": 10, "actions": {"mic": "on"}}]}
// after
{"transitions": [{"client_ts_ms": 10, "actions": ["mic:on"]}]} Defensive patterns
Strategy: validation
Validate before calling
for t in payload.get('transitions', []):
if not isinstance(t.get('actions'), list):
raise ValueError('transition actions must be a list before parsing') Type guard
def has_valid_transitions(payload: dict) -> bool:
return isinstance(payload.get('transitions'), list) and all(
isinstance(t, dict) and isinstance(t.get('actions'), list)
for t in payload['transitions']
) Try / catch
try:
parse_control_event_payload(raw)
except ValueError as e:
drop_or_alert_malformed_event(raw, e) Prevention
- Validate control-event payloads against a schema at the producer boundary.
- Log rejected payloads with their raw JSON to find which producer emitted the bad shape.
When it happens
Trigger: Calling parse_control_event_payload with a payload where transitions[i]['actions'] is missing, null, a dict, or a string instead of a JSON array.
Common situations: Upstream producer changes the control-event schema (actions renamed, emitted as an object map keyed by action name); hand-crafted test payloads omitting actions; JSON where actions is null.
Related errors
- {kind} state payload requires transitions
- {kind} transition must be a map
- prompt event payload must be a string
- camera_actions event payload must be list[list[str]]
- unsupported event kind: {event.kind}
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/1deab690bb4b972e.
Report an issue: GitHub.