calesthio/OpenMontage · error · ValueError

face_choose items must be strings or objects

Error message

face_choose items must be strings or objects

What it means

Raised by KlingLipSyncTool._normalize_face_choose when an entry in the face_choose list is neither a string nor a dict. The tool accepts face_choose as a single dict, a list of face-id strings, or a list of objects carrying face_id/id plus per-face timing fields; any other element type (int, None, tuple, nested list) is rejected before the Kling API is called.

Source

Thrown at tools/avatar/kling_lip_sync.py:410

        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:
            return explicit, {
                "selection_method": "user_selected",
                "selection_reason": "face_choose or face_id was provided",
                "selected_face": explicit,
            }
        if len(faces) == 1:

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Use face-id strings: face_choose=["face_1", "face_2"]
  2. Or use objects with face_id or id keys: face_choose=[{"face_id": "face_1", "sound_insert_time": 0}]
  3. If you only have numeric face indexes, map them to the face_id values returned by identify_face first
  4. A single dict is also accepted and auto-wrapped: face_choose={"face_id": "face_1"}

Example fix

# before
inputs = {"face_choose": [0, 1]}

# after
inputs = {"face_choose": ["face_1", "face_2"]}
Defensive patterns

Strategy: validation

Validate before calling

def valid_face_choose(fc):
    if fc is None:
        return True
    if isinstance(fc, dict):
        fc = [fc]
    if not isinstance(fc, list):
        return False
    return all(isinstance(i, str) or (isinstance(i, dict) and (i.get("face_id") or i.get("id"))) for i in fc)

if not valid_face_choose(inputs.get("face_choose")):
    raise ValueError("face_choose must contain face-id strings or objects with face_id")

Type guard

def is_face_choose_item(v) -> bool:
    return isinstance(v, str) or (isinstance(v, dict) and bool(v.get("face_id") or v.get("id")))

Try / catch

try:
    tool.run(inputs)
except ValueError as e:
    if "face_choose" in str(e):
        fix_face_choose_and_retry(inputs)  # normalize items to {"face_id": str}
    raise

Prevention

When it happens

Trigger: Calling kling_lip_sync with inputs like face_choose=[123, "abc"], face_choose=[None], or face_choose=[["face1"]] (a nested list). The loop in _normalize_face_choose hits the else branch for the first non-str/non-dict element and raises ValueError immediately.

Common situations: Passing face indexes instead of face ids (face_choose=[0, 1]), copying a JSON payload where face ids were numbers, or a template/agent emitting null entries for undetected faces.

Related errors


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