calesthio/OpenMontage · error · ValueError

advanced_lip_sync currently supports exactly one face_choose

Error message

advanced_lip_sync currently supports exactly one face_choose item

What it means

ValueError from _build_advanced_request when the normalized face_choose list has zero or more than one entries. The underlying Kling advanced-lip-sync binding in this client handles exactly one face per call; multi-face or empty selections are rejected before the request is sent.

Source

Thrown at tools/avatar/kling_lip_sync.py:372

        if inputs.get("video_url"):
            payload["video_url"] = str(inputs["video_url"])
        if not payload:
            raise ValueError("Kling identify_face requires video_id or video_url")
        return {
            "path": "/v1/videos/identify-face",
            "payload": payload,
            "operation": "identify_face",
        }

    def _build_advanced_request(self, inputs: dict[str, Any]) -> dict[str, Any]:
        session_id = str(inputs.get("session_id") or "").strip()
        if not session_id:
            raise ValueError("advanced_lip_sync requires session_id")
        face_choose = self._normalize_face_choose(inputs)
        if not face_choose:
            raise ValueError("advanced_lip_sync requires face_choose or face_id")
        if len(face_choose) != 1:
            raise ValueError("advanced_lip_sync currently supports exactly one face_choose item")
        face_item = face_choose[0]
        audio_source = self._copy_audio_input(inputs, face_item)
        self._copy_timing_fields(inputs, face_item)
        payload: dict[str, Any] = {
            "session_id": session_id,
            "face_choose": face_choose,
        }
        self._copy_common_task_fields(inputs, payload)
        return {
            "protocol": "classic",
            "path": "/v1/videos/advanced-lip-sync",
            "payload": payload,
            "operation": "advanced_lip_sync",
            "model": "kling-official-lip-sync",
            "audio_source": audio_source,
        }

    @staticmethod

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Pick one face per call: pass a single-item face_choose or a bare face_id.
  2. To animate multiple faces, run one advanced_lip_sync call per face (with the same session_id) and merge outputs afterwards.
  3. Empty list means the selection step failed — re-read the identify result and choose a face.

Example fix

// before
result = tool.run({..., "face_choose": [{"face_id": "1"}, {"face_id": "2"}]})

// after
r1 = tool.run({..., "face_choose": [{"face_id": "1"}]})
r2 = tool.run({..., "face_choose": [{"face_id": "2"}]})
Defensive patterns

Strategy: validation

Validate before calling

face_choose = inputs.get("face_choose") or ([{"face_id": str(inputs["face_id"])}] if inputs.get("face_id") else [])
assert len(face_choose) == 1, "one face per advanced_lip_sync call"

Type guard

def is_single_face_selection(face_choose: list) -> bool:
    return len(face_choose) == 1

Prevention

When it happens

Trigger: Passing face_choose with 2+ items (multi-person video) to one advanced_lip_sync call; passing an empty list explicitly.

Common situations: Identify found several faces and the caller forwards all of them; attempting batch animation in a single request.

Related errors


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