nexu-io/open-design · error · ValueError

Participant {speaker} has conflicting transcript avatar hint

Error message

Participant {speaker} has conflicting transcript avatar hints: {participant['avatarKey']} and {message['avatar']}; set a config preset to override

What it means

Thrown in build_spec() when a speaker has multiple transcript lines whose inline avatar hints disagree, and no config 'preset' override exists to pin a single avatar. Without a config preset the builder cannot know which hint wins, so it refuses rather than silently picking one.

Source

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

            participant = {
                "id": unique_slug(slugify(speaker), used_participant_ids),
                "name": speaker,
                "side": side,
                "avatarKey": avatar_key,
            }
            used_participant_ids.add(participant["id"])
            if config["avatarMode"] in {"upload", "mixed"} and configured.get("uploadPath"):
                participant["uploadPath"] = configured["uploadPath"]
            if config["avatarMode"] == "upload" and not participant.get("uploadPath"):
                raise ValueError(f"avatarMode=upload requires uploadPath for participant {speaker}")
            participants[speaker] = participant
        participant = participants[speaker]
        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"],

View on GitHub (pinned to 5be4028344)

Solutions

  1. Set a single 'preset' under config.participants for that speaker; config preset wins and transcript hints are ignored.
  2. Make all transcript avatar hints for that speaker identical and a valid PRESET_KEYS value.
  3. Remove the avatar field from the conflicting transcript lines so the auto-assignment path is used.

Example fix

// before
// transcript:
Bob|left|female-fox-yellow|hi
Bob|left|male-bear-mint|yo
// config has no Bob preset -> after (config)
{"participants": {"Bob": {"side": "left", "preset": "female-fox-yellow"}}}
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"}
def check_avatar_hint_consistency(transcript_path, config):
    configured = {n for n, p in config.get("participants", {}).items() if p.get("preset")}
    hints = {}
    for raw in transcript_path.read_text(encoding="utf-8").splitlines():
        parts = [p.strip() for p in raw.split("|")] if "|" in raw else []
        if len(parts) < 4:
            continue
        avatar, speaker = parts[-2], parts[0]
        if avatar not in PRESET_KEYS or speaker in configured:
            continue
        if speaker in hints and hints[speaker] != avatar:
            raise ValueError(f"{speaker} has conflicting avatar hints; set a config preset")
        hints[speaker] = avatar

Prevention

When it happens

Trigger: Two transcript lines for the same speaker use different avatar tokens in the avatar slot (e.g. `Bob|left|female-fox-yellow|hi` and `Bob|left|male-bear-mint|yo`), and config.participants has no 'preset' for that speaker.

Common situations: Copy-pasting message lines from different templates; partially editing avatar hints; intending per-message avatar variety (which the spec does not support without a config preset).

Related errors


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