calesthio/OpenMontage · error · ValueError

face_choose items must include face_id

Error message

face_choose items must include face_id

What it means

ValueError from _normalize_face_choose when a dict item in the face_choose list carries neither face_id nor id. String items are converted to {'face_id': item} automatically, but object items must identify the face themselves — the normalizer even maps legacy 'id' to 'face_id', so an item missing both has nothing to bind the choice to.

Source

Thrown at tools/avatar/kling_lip_sync.py:404

            "model": "kling-official-lip-sync",
            "audio_source": audio_source,
        }

    @staticmethod
    def _normalize_face_choose(inputs: dict[str, Any]) -> list[dict[str, Any]]:
        if inputs.get("face_choose"):
            raw = inputs["face_choose"]
            if isinstance(raw, dict):
                raw = [raw]
            if not isinstance(raw, list):
                raise ValueError("face_choose must be a list of face choice objects")
            normalized: list[dict[str, Any]] = []
            for item in raw:
                if isinstance(item, str):
                    normalized.append({"face_id": item})
                elif isinstance(item, dict):
                    if not (item.get("face_id") or item.get("id")):
                        raise ValueError("face_choose items must include face_id")
                    record = dict(item)
                    if "face_id" not in record and record.get("id"):
                        record["face_id"] = record.pop("id")
                    normalized.append(record)
                else:
                    raise ValueError("face_choose items must be strings or objects")
            return normalized
        if inputs.get("face_id"):
            return [{"face_id": str(inputs["face_id"])}]
        return []

    def _face_selection(
        self,
        faces: list[dict[str, Any]],
        inputs: dict[str, Any],
    ) -> tuple[list[dict[str, Any]], dict[str, Any]]:
        explicit = self._normalize_face_choose(inputs)
        if explicit:

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Add the identifier: {'face_id': '1', ...} — use the face_id values from the identify_face result.
  2. If the source records use 'id', that is accepted too and normalized automatically.
  3. Bare strings in the list also work: ['1'].

Example fix

// before
result = tool.run({..., "face_choose": [{"face_index": 0, "lb": 1.0}]})

// after
result = tool.run({..., "face_choose": [{"face_id": "1", "face_index": 0, "lb": 1.0}]})
Defensive patterns

Strategy: validation

Validate before calling

for item in face_choose:
    if isinstance(item, dict):
        assert item.get("face_id") or item.get("id"), \
            f"face_choose item missing face_id: {item}"

Type guard

def item_has_face_id(item: dict) -> bool:
    return bool(item.get("face_id") or item.get("id"))

Prevention

When it happens

Trigger: Passing face_choose=[{'face_index': 0}] or [{...timing fields...}] with the identifier omitted; forwarding raw identify-face entries whose id lives under yet another key.

Common situations: Developer copies a face record from the identify artifact but the id field name differs (e.g. 'faceID' or 'index'); LLM-generated face_choose omitting the id.

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/605a937633163cca. Report an issue: GitHub.