{"record":{"id":"28e11e7a5de0d409","repo":"sgl-project/sglang","slug":"invalid-action-segment-segment-r-expected-key","errorCode":null,"errorMessage":"invalid action segment {segment!r}; expected '<keys>-<frames>'","messagePattern":"invalid action segment (.+?); expected '<keys>-<frames>'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/base.py","lineNumber":128,"sourceCode":"    if not isinstance(payload, list):\n        raise ValueError(f\"{error_label} must be list[list[str]]\")\n    out: list[list[str]] = []\n    for frame_actions in payload:\n        if not isinstance(frame_actions, list):\n            raise ValueError(f\"{error_label} must be list[list[str]]\")\n        out.append([str(key).lower() for key in frame_actions])\n    return out\n\n\ndef parse_sana_wm_action_string(action: str) -> list[list[str]]:\n    cleaned = \"\".join(action.replace(\"，\", \",\").split())\n    if not cleaned:\n        raise ValueError(\"action string is empty\")\n\n    per_frame: list[list[str]] = []\n    for segment in cleaned.split(\",\"):\n        if not segment or \"-\" not in segment:\n            raise ValueError(\n                f\"invalid action segment {segment!r}; expected '<keys>-<frames>'\"\n            )\n        keys_part, duration = segment.rsplit(\"-\", 1)\n        if not duration.isdigit() or int(duration) <= 0:\n            raise ValueError(f\"invalid duration in action segment {segment!r}\")\n\n        if keys_part.lower() == \"none\":\n            keys: list[str] = []\n        else:\n            bad = sorted(\n                {\n                    char\n                    for char in keys_part.lower()\n                    if char not in _SANA_WM_ALLOWED_ACTION_KEYS\n                }\n            )\n            if bad:\n                raise ValueError(","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/base.py#L110-L146","documentation":"Raised by parse_sana_wm_action_string when a comma-separated segment is empty or contains no '-' separator. Each segment must follow '<keys>-<frames>', e.g. 'ab-4'.","triggerScenarios":"Action strings like 'ab4' (missing dash), 'ab-4,,cd-2' (empty segment from trailing/double comma), or 'ab-' / '-4' style malformed segments (though those fail the dash check only when no '-' exists at all).","commonSituations":"User-typed camera action strings missing the dash; trailing commas in config; LLM-generated action strings omitting the duration suffix.","solutions":["Fix the segment to '<keys>-<frames>' form, e.g. 'ab-4'","Strip trailing/duplicate commas before parsing: ','.join(s for s in cleaned.split(',') if s)","Validate the format with a regex before handing to the pipeline"],"exampleFix":"// before\naction = 'forward-4,,left-2,'\n// after\naction = 'forward-4,left-2'","handlingStrategy":"validation","validationCode":"import re\nassert all(re.fullmatch(r'.+-.+', seg) for seg in cleaned.split(',') if seg)","typeGuard":"def segments_wellformed(s: str) -> bool:\n    return all('-' in seg and seg for seg in ''.join(s.replace('，', ',')).split(','))","tryCatchPattern":null,"preventionTips":["Strip trailing/double commas from user input before parsing","Document the '<keys>-<frames>' grammar at the user-facing API"],"tags":["validation","action-string","sana-wm"],"backgroundTag":"malformed-input-format","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}