{"record":{"id":"ac4fbb2b640e7f67","repo":"sgl-project/sglang","slug":"unknown-action-keys-bad-allowed-keys-are-sorte","errorCode":null,"errorMessage":"unknown action keys {bad}; allowed keys are {sorted(_SANA_WM_ALLOWED_ACTION_KEYS)}","messagePattern":"unknown action keys (.+?); allowed keys are (.+?)","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":146,"sourceCode":"            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(\n                    f\"unknown action keys {bad}; allowed keys are \"\n                    f\"{sorted(_SANA_WM_ALLOWED_ACTION_KEYS)}\"\n                )\n            keys = sorted(set(keys_part.lower()))\n        # Fresh list per frame: repeated frames must NOT alias one list object\n        # (callers could mutate a frame and silently edit all its repeats).\n        per_frame.extend([list(keys) for _ in range(int(duration))])\n    return per_frame\n\n\ndef sana_wm_action_to_camera_to_world_array(\n    action: str,\n    *,\n    translation_speed: float = _SANA_WM_BIDIRECTIONAL_DEFAULT_TRANSLATION_SPEED,\n    rotation_speed_deg: float = _SANA_WM_DEFAULT_ROTATION_SPEED_DEG,\n    pitch_limit_deg: float = _SANA_WM_DEFAULT_PITCH_LIMIT_DEG,\n    strafe_yaw_coupling: float = 0.4,\n) -> np.ndarray:","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/base.py#L128-L164","documentation":"Raised by parse_sana_wm_action_string when the keys part of a segment contains characters outside _SANA_WM_ALLOWED_ACTION_KEYS (after lowercasing). Only the whitelisted single-letter camera action keys are accepted; 'none' is the special empty-key form.","triggerScenarios":"Segments like 'xy-4' where 'x'/'y' are not allowed keys, or misspelled words like 'forward-4' (each letter 'f','o','r','w','a','d' is checked individually).","commonSituations":"Users writing word commands ('left','zoom') instead of the letter key shorthand; typos in key letters; LLM emitting invented keys.","solutions":["Use only allowed keys — check sorted(_SANA_WM_ALLOWED_ACTION_KEYS) in the module","Use word 'none' (lowercase) for no-action segments","Map user words to keys before building the string, e.g. {'left':'a','right':'d'}"],"exampleFix":"// before\naction = 'forward-4'\n// after\naction = 'f-4'  # or whatever keys are in _SANA_WM_ALLOWED_ACTION_KEYS","handlingStrategy":"validation","validationCode":"from sglang.multimodal_gen.runtime.pipelines_core.stages.model_specific_stages.sana_wm.base import _SANA_WM_ALLOWED_ACTION_KEYS as K\nassert set(''.join(keys)) <= K","typeGuard":"def keys_allowed(keys_part: str, allowed: set[str]) -> bool:\n    return keys_part.lower() == 'none' or set(keys_part.lower()) <= allowed","tryCatchPattern":null,"preventionTips":["Expose the allowed key set in your API docs/UI","Map natural-language commands to canonical keys server-side"],"tags":["validation","action-string","whitelist","sana-wm"],"backgroundTag":"value-not-in-allowed-set","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}