nexu-io/open-design · error · ValueError
Unsupported preset for participant {speaker}: {preset_key}
Error message
Unsupported preset for participant {speaker}: {preset_key} What it means
Raised by validate_config during participant iteration when a participant's `preset` field is present and not one of PRESET_KEYS = [female-bunny-pink, female-cat-orange, female-fox-yellow, male-bear-mint, male-penguin-blue, male-koala-lilac]. The check is skipped if preset is omitted (avatarMode='upload' participants need no preset), but any non-empty unrecognized preset string triggers it.
Source
Thrown at skills/chat-motion-overlay/scripts/build_chat_overlay_spec.py:141
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:
preferred = [*PRESET_KEYS[participant_index:], *PRESET_KEYS[:participant_index]]
for avatar_key in preferred:
if avatar_key not in used_avatar_keys:
return avatar_key
return PRESET_KEYS[participant_index % len(PRESET_KEYS)]
def configured_participant(speaker: str, config: dict) -> dict:View on GitHub (pinned to 5be4028344)
Solutions
- Use one of the exact preset keys: female-bunny-pink, female-cat-orange, female-fox-yellow, male-bear-mint, male-penguin-blue, male-koala-lilac.
- For a custom avatar, switch avatarMode to 'upload' and supply uploadPath instead of preset.
- Lowercase exact match required; verify spelling against PRESET_KEYS in the script.
- Pre-validate: `PRESET={'female-bunny-pink',...}; for p in c.get('participants',{}).values(): assert not p.get('preset') or p['preset'] in PRESET`.
Example fix
// before
{"avatarMode": "preset", "participants": {"Alice": {"preset": "cat"}}}
# -> ValueError: Unsupported preset for participant Alice: cat
// after
{"avatarMode": "preset", "participants": {"Alice": {"preset": "female-cat-orange"}}} Defensive patterns
Strategy: validation
Validate before calling
PRESET_KEYS = {
"female-bunny-pink", "female-cat-orange", "female-fox-yellow",
"male-bear-mint", "male-penguin-blue", "male-koala-lilac",
}
for speaker, participant in config.get("participants", {}).items():
preset = participant.get("preset")
if preset and preset not in PRESET_KEYS:
raise SystemExit(
f"Participant {speaker} preset must be one of {sorted(PRESET_KEYS)}; "
f"got {preset!r}. For custom avatars use avatarMode='upload' + uploadPath."
) Type guard
def is_preset_key(value) -> bool:
return value in {
"female-bunny-pink", "female-cat-orange", "female-fox-yellow",
"male-bear-mint", "male-penguin-blue", "male-koala-lilac",
} Try / catch
try:
validate_config(config)
except ValueError as exc:
raise SystemExit(f"Config error: {exc}") from exc Prevention
- Use exact preset keys from PRESET_KEYS (lowercase, hyphenated).
- For custom avatars, use avatarMode='upload' with uploadPath rather than inventing presets.
- Validate presets in one pass with the other participant checks.
- Regenerate docs/help from PRESET_KEYS so the list stays accurate.
When it happens
Trigger: User sets `"preset": "cat"`, `"preset": "alice"`, `"preset": "female-bunny"` (truncated), or invents a name not in the six-key roster. Also fires on typos and case mismatches ('Female-Bunny-Pink').
Common situations: Wanting a custom avatar and setting a preset key for it (custom avatars go through uploadPath + avatarMode='upload', not preset); partial copy of a key; case sensitivity; outdated preset list from a previous version.
Related errors
- Unsupported avatarMode: {config['avatarMode']}
- Unsupported side for participant {speaker}: {side}
- Participant {speaker} config must use uploadPath, not upload
- Unsupported container: {config['container']}
- Unsupported deviceFrame: {config['deviceFrame']}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/c74497948262e09f.
Report an issue: GitHub.