nexu-io/open-design · error · ValueError

avatarMode=mixed requires at least one upload path

Error message

avatarMode=mixed requires at least one upload path

What it means

Thrown at the end of build_spec() when avatarMode is 'mixed' but none of the resolved participants carry an uploadPath. Mixed mode's contract is that at least one participant uses an uploaded avatar while others may use presets; if all end up preset-based, the mode is wrong.

Source

Thrown at skills/chat-motion-overlay/scripts/build_chat_overlay_spec.py:214

        side = message["side"] or participant["side"]
        if side != participant["side"]:
            raise ValueError(f"Participant {speaker} appears on both {participant['side']} and {side}; use one side per participant")
        avatar_key = participant["avatarKey"]
        if message["avatar"] and message["avatar"] != participant["avatarKey"] and not configured_participant(speaker, config).get("preset"):
            raise ValueError(f"Participant {speaker} has conflicting transcript avatar hints: {participant['avatarKey']} and {message['avatar']}; set a config preset to override")
        messages.append(
            {
                "id": f"msg-{index + 1}",
                "speaker": speaker,
                "text": message["text"].strip(),
                "side": side,
                "avatarKey": avatar_key,
                "appearAt": start + index * gap,
                "highlight": bool(message["highlight"]),
            }
        )
    if config["avatarMode"] == "mixed" and not any(participant.get("uploadPath") for participant in participants.values()):
        raise ValueError("avatarMode=mixed requires at least one upload path")
    duration = start + max(len(messages) - 1, 0) * gap + int(meta["hold"])
    runtime_output = DELIVERY_TO_OUTPUT[config["deliveryFormat"]]
    scene_config = {key: value for key, value in config.items() if key != "participants"}
    return {
        "title": meta["title"],
        "timestamp": meta["time"],
        "durationInFrames": duration,
        "timing": {"start": start, "gap": gap, "hold": int(meta["hold"])},
        "sceneConfig": {**scene_config, "output": runtime_output},
        "participants": list(participants.values()),
        "messages": messages,
    }


def slugify(text: str) -> str:
    lowered = text.strip().lower()
    return re.sub(r"[^a-z0-9\u4e00-\u9fff]+", "-", lowered).strip("-") or "speaker"

View on GitHub (pinned to 5be4028344)

Solutions

  1. Give at least one participant (that actually appears in the transcript) an 'uploadPath' pointing at an existing image.
  2. Switch avatarMode to 'preset' if no uploads are needed.
  3. Switch avatarMode to 'upload' if every participant should have an uploadPath.

Example fix

// before
{"avatarMode": "mixed", "participants": {"闺蜜": {"preset": "female-bunny-pink"}, "老婆": {"preset": "female-cat-orange"}}}
// after
{"avatarMode": "mixed", "participants": {"闺蜜": {"uploadPath": "/me.png"}, "老婆": {"preset": "female-cat-orange"}}}
Defensive patterns

Strategy: validation

Validate before calling

def mixed_needs_an_upload(config, transcript_speakers):
    if config.get("avatarMode") != "mixed":
        return
    has_upload = any(config.get("participants", {}).get(s, {}).get("uploadPath") for s in transcript_speakers)
    if not has_upload:
        raise ValueError("avatarMode=mixed requires at least one transcript speaker with uploadPath")

Prevention

When it happens

Trigger: avatarMode='mixed' with a config where no participant has uploadPath, or where upload-bearing participants never appear in the transcript so they are never instantiated. The run_test_matrix.py case 'invalid_mixed_without_upload' reproduces this.

Common situations: Setting avatarMode='mixed' as a 'safe default' without actually supplying an upload; configuring an upload participant under a speaker name that is misspelled relative to the transcript; deleting the only upload participant but leaving the mode as mixed.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/83ea6c55417de225. Report an issue: GitHub.