nexu-io/open-design · error · ValueError
container=none does not support deviceFrame=iphone-dynamic-i
Error message
container=none does not support deviceFrame=iphone-dynamic-island; use deviceFrame=none or choose an app container
What it means
Raised by validate_config as a cross-field rule: when container='none' AND deviceFrame='iphone-dynamic-island', the combination is rejected. The iPhone dynamic-island frame requires an app container to render into; without one, the island has nowhere to sit. Either set deviceFrame='none' or pick a real container (wechat/telegram/messenger).
Source
Thrown at skills/chat-motion-overlay/scripts/build_chat_overlay_spec.py:133
user = json.loads(Path(path).read_text(encoding="utf-8"))
config.update(user)
validate_config(config)
return config
def validate_config(config: dict) -> None:
if config["container"] not in ALLOWED_CONTAINERS:
raise ValueError(f"Unsupported container: {config['container']}")
if config["avatarMode"] not in ALLOWED_AVATAR_MODES:
raise ValueError(f"Unsupported avatarMode: {config['avatarMode']}")
if config["deviceFrame"] not in ALLOWED_DEVICE_FRAMES:
raise ValueError(f"Unsupported deviceFrame: {config['deviceFrame']}")
if config["nicknameMode"] not in ALLOWED_NICKNAME_MODES:
raise ValueError(f"Unsupported nicknameMode: {config['nicknameMode']}")
if config["deliveryFormat"] not in ALLOWED_DELIVERY_FORMATS:
raise ValueError(f"Unsupported deliveryFormat: {config['deliveryFormat']}")
if config["container"] == "none" and config["deviceFrame"] == "iphone-dynamic-island":
raise ValueError("container=none does not support deviceFrame=iphone-dynamic-island; use deviceFrame=none or choose an app container")
for speaker, participant in config.get("participants", {}).items():
side = participant.get("side")
if side and side not in {"left", "right"}:
raise ValueError(f"Unsupported side for participant {speaker}: {side}")
preset_key = participant.get("preset")
if preset_key and preset_key not in PRESET_KEYS:
raise ValueError(f"Unsupported preset for participant {speaker}: {preset_key}")
upload_path = participant.get("uploadPath")
if participant.get("uploadAsset"):
raise ValueError(f"Participant {speaker} config must use uploadPath, not uploadAsset")
if config["avatarMode"] == "preset" and upload_path:
raise ValueError(f"avatarMode=preset does not allow uploadPath for participant {speaker}")
if config["avatarMode"] == "upload" and not upload_path:
raise ValueError(f"avatarMode=upload requires uploadPath for participant {speaker}")
def auto_avatar_for_participant(participant_index: int, used_avatar_keys: set[str]) -> str:View on GitHub (pinned to 5be4028344)
Solutions
- Set deviceFrame to 'none' when container is 'none': `{container: none, deviceFrame: none}`.
- Or keep deviceFrame='iphone-dynamic-island' and choose a container: wechat/telegram/messenger.
- Review both fields together — they are coupled; do not change one without considering the other.
- Pre-validate the pair: `assert not (c['container']=='none' and c['deviceFrame']=='iphone-dynamic-island')`.
Example fix
// before
{"container": "none", "deviceFrame": "iphone-dynamic-island"}
# -> ValueError: container=none does not support deviceFrame=iphone-dynamic-island ...
// after (option A: no chrome, no frame)
{"container": "none", "deviceFrame": "none"}
// after (option B: keep the island, add a container)
{"container": "telegram", "deviceFrame": "iphone-dynamic-island"} Defensive patterns
Strategy: validation
Validate before calling
if config["container"] == "none" and config["deviceFrame"] == "iphone-dynamic-island":
raise SystemExit(
"container=none cannot use deviceFrame=iphone-dynamic-island. "
"Set deviceFrame=none, or pick a container (wechat/telegram/messenger)."
) Try / catch
try:
validate_config(config)
except ValueError as exc:
raise SystemExit(f"Config error: {exc}") from exc Prevention
- Treat container and deviceFrame as a coupled pair when editing config.
- When disabling chrome (container=none), audit deviceFrame at the same time.
- Encode cross-field rules in jsonschema (if/then) so they fail at load.
- Provide a config preset library that bundles known-good combinations.
When it happens
Trigger: User wants a bare chat with no app chrome but keeps the default deviceFrame 'iphone-dynamic-island', producing `{container: none, deviceFrame: iphone-dynamic-island}`. This is an easy mistake because both defaults individually seem reasonable.
Common situations: Copy-pasting DEFAULT_CONFIG and only flipping container to 'none' while leaving deviceFrame at its default; wanting a 'minimal' look and disabling the container without realizing the frame depends on it; merging two partial config files where each independently is valid but together violate the cross rule.
Related errors
- Unsupported deviceFrame: {config['deviceFrame']}
- Unsupported container: {config['container']}
- Unsupported avatarMode: {config['avatarMode']}
- Unsupported nicknameMode: {config['nicknameMode']}
- Unsupported deliveryFormat: {config['deliveryFormat']}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/ff5a40198efd56f4.
Report an issue: GitHub.