nexu-io/open-design · error · ValueError
avatarMode=preset does not allow uploadPath for participant
Error message
avatarMode=preset does not allow uploadPath for participant {speaker} What it means
Thrown by validate_config() in build_chat_overlay_spec.py when avatarMode is 'preset' but a participant also supplies an 'uploadPath'. Preset mode draws avatars exclusively from the built-in PRESET_KEYS library, so an upload path is contradictory. It is a config-level guard that fires before any transcript parsing or spec building.
Source
Thrown at skills/chat-motion-overlay/scripts/build_chat_overlay_spec.py:146
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:
return config.get("participants", {}).get(speaker, {})
def build_spec(parsed: dict, config: dict) -> dict:
meta = parsed["metadata"]View on GitHub (pinned to 5be4028344)
Solutions
- Set "avatarMode" to "upload" or "mixed" in the config if you intend to use the uploaded avatar.
- Remove the "uploadPath" key from every participant entry when you want to keep avatarMode='preset'.
- Audit the participants dict: only 'side', 'preset', and (for upload/mixed) 'uploadPath' are honored; drop stray uploadAsset/uploadPath you did not mean to set.
Example fix
// before
{"avatarMode": "preset", "participants": {"老婆": {"side": "right", "preset": "female-cat-orange", "uploadPath": "/abs/me.png"}}}
// after
{"avatarMode": "preset", "participants": {"老婆": {"side": "right", "preset": "female-cat-orange"}}} Defensive patterns
Strategy: validation
Validate before calling
def preset_mode_has_no_upload_paths(config: dict) -> None:
if config.get("avatarMode") != "preset":
return
offenders = [name for name, p in config.get("participants", {}).items() if p.get("uploadPath")]
if offenders:
raise ValueError(f"avatarMode=preset forbids uploadPath; offenders: {offenders}")
preset_mode_has_no_upload_paths(config) Prevention
- Keep a single config schema/source of truth and validate avatarMode vs uploadPath presence in a preflight step.
- When reusing a config, switch avatarMode and participants together; never one without the other.
When it happens
Trigger: Run build_chat_overlay_spec.py --config config.json (or prepare_chat_overlay_bundle.py, which calls load_config) where the JSON has "avatarMode": "preset" and any entry under "participants" contains an "uploadPath" key with a truthy value.
Common situations: Copying a config block from an 'upload' or 'mixed' example and forgetting to switch avatarMode; iterating on configs and leaving a stale uploadPath; misunderstanding that preset mode forbids uploads entirely rather than ignoring them.
Related errors
- avatarMode=upload requires uploadPath for participant {speak
- avatarMode=mixed requires at least one upload path
- Unsupported avatar key for participant {speaker}: {avatar_ke
- Configured uploadPath for participant {participant['name']}
- Participant {speaker} appears on both {participant['side']}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/13dec3d5d78171c6.
Report an issue: GitHub.