{"record":{"id":"1756a30db9e59289","repo":"nexu-io/open-design","slug":"unsupported-deviceframe-config-deviceframe","errorCode":null,"errorMessage":"Unsupported deviceFrame: {config['deviceFrame']}","messagePattern":"Unsupported deviceFrame: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/chat-motion-overlay/scripts/build_chat_overlay_spec.py","lineNumber":127,"sourceCode":"\ndef load_config(path: str | None) -> dict:\n    config = json.loads(json.dumps(DEFAULT_CONFIG))\n    if not path:\n        validate_config(config)\n        return config\n    user = json.loads(Path(path).read_text(encoding=\"utf-8\"))\n    config.update(user)\n    validate_config(config)\n    return config\n\n\ndef validate_config(config: dict) -> None:\n    if config[\"container\"] not in ALLOWED_CONTAINERS:\n        raise ValueError(f\"Unsupported container: {config['container']}\")\n    if config[\"avatarMode\"] not in ALLOWED_AVATAR_MODES:\n        raise ValueError(f\"Unsupported avatarMode: {config['avatarMode']}\")\n    if config[\"deviceFrame\"] not in ALLOWED_DEVICE_FRAMES:\n        raise ValueError(f\"Unsupported deviceFrame: {config['deviceFrame']}\")\n    if config[\"nicknameMode\"] not in ALLOWED_NICKNAME_MODES:\n        raise ValueError(f\"Unsupported nicknameMode: {config['nicknameMode']}\")\n    if config[\"deliveryFormat\"] not in ALLOWED_DELIVERY_FORMATS:\n        raise ValueError(f\"Unsupported deliveryFormat: {config['deliveryFormat']}\")\n    if config[\"container\"] == \"none\" and config[\"deviceFrame\"] == \"iphone-dynamic-island\":\n        raise ValueError(\"container=none does not support deviceFrame=iphone-dynamic-island; use deviceFrame=none or choose an app container\")\n\n    for speaker, participant in config.get(\"participants\", {}).items():\n        side = participant.get(\"side\")\n        if side and side not in {\"left\", \"right\"}:\n            raise ValueError(f\"Unsupported side for participant {speaker}: {side}\")\n        preset_key = participant.get(\"preset\")\n        if preset_key and preset_key not in PRESET_KEYS:\n            raise ValueError(f\"Unsupported preset for participant {speaker}: {preset_key}\")\n        upload_path = participant.get(\"uploadPath\")\n        if participant.get(\"uploadAsset\"):\n            raise ValueError(f\"Participant {speaker} config must use uploadPath, not uploadAsset\")\n        if config[\"avatarMode\"] == \"preset\" and upload_path:","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/chat-motion-overlay/scripts/build_chat_overlay_spec.py#L109-L145","documentation":"Raised by validate_config when config['deviceFrame'] is not in ALLOWED_DEVICE_FRAMES = {\"none\",\"iphone-dynamic-island\"}. Default is 'iphone-dynamic-island'. Note the cross-field constraint: container='none' combined with deviceFrame='iphone-dynamic-island' is rejected separately (error 576).","triggerScenarios":"User sets `\"deviceFrame\": \"pixel\"`, `\"deviceFrame\": \"iphone-notch\"`, `\"deviceFrame\": \"android\"`, or any frame name not in the allowed set.","commonSituations":"Wanting an Android frame not yet supported; guessing iPhone variant names ('notch', 'xs'); typo; legacy config; combining 'none' container with a frame that the script does not whitelist.","solutions":["Set deviceFrame to one of: none, iphone-dynamic-island.","If container is 'none', you MUST use deviceFrame 'none' (iphone-dynamic-island requires an app container — see error 576).","For an unsupported device frame, request it upstream or render without a frame (deviceFrame=none).","Pre-validate: `assert c['deviceFrame'] in {'none','iphone-dynamic-island'}`."],"exampleFix":"// before\n{\"deviceFrame\": \"pixel\"}\n# -> ValueError: Unsupported deviceFrame: pixel\n\n// after\n{\"deviceFrame\": \"iphone-dynamic-island\", \"container\": \"wechat\"}","handlingStrategy":"validation","validationCode":"ALLOWED_DEVICE_FRAMES = {\"none\", \"iphone-dynamic-island\"}\n\nif config[\"deviceFrame\"] not in ALLOWED_DEVICE_FRAMES:\n    raise SystemExit(\n        f\"deviceFrame must be one of {sorted(ALLOWED_DEVICE_FRAMES)}; \"\n        f\"got {config['deviceFrame']!r}\"\n    )","typeGuard":"def is_device_frame(value) -> bool:\n    return value in {\"none\", \"iphone-dynamic-island\"}","tryCatchPattern":"try:\n    validate_config(config)\nexcept ValueError as exc:\n    raise SystemExit(f\"Config error: {exc}\") from exc","preventionTips":["Remember deviceFrame is coupled to container (none container requires none frame).","Use the exact lowercase token 'iphone-dynamic-island'.","Catch this at config load via jsonschema enum.","If you need an unsupported frame, render with 'none' and composite externally."],"tags":["config","validation","chat-overlay","device-frame"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}