calesthio/OpenMontage · error · ValueError

advanced_lip_sync requires face_choose or face_id

Error message

advanced_lip_sync requires face_choose or face_id

What it means

ValueError from _build_advanced_request when _normalize_face_choose returns an empty list: inputs contained neither face_choose nor face_id. After identify_face reports the faces it found, the advanced call must say which face(s) to animate; omitting the selection is rejected client-side.

Source

Thrown at tools/avatar/kling_lip_sync.py:370

        if inputs.get("video_id"):
            payload["video_id"] = str(inputs["video_id"])
        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,
        }

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Read the faces from the identify_face result (or the faces artifact file) and pass one as face_id.
  2. Simplest form: face_id='1' for the first detected face.
  3. Full form: face_choose=[{'face_id': '1', ...timing fields...}].

Example fix

// before
result = tool.run({"operation": "advanced_lip_sync", "session_id": sid, "audio_path": "a.mp3"})

// after
result = tool.run({"operation": "advanced_lip_sync", "session_id": sid, "face_id": "1", "audio_path": "a.mp3"})
Defensive patterns

Strategy: validation

Validate before calling

assert inputs.get("face_choose") or inputs.get("face_id"), \
    "advanced_lip_sync needs a face selection from the identify result"

Prevention

When it happens

Trigger: Calling advanced_lip_sync with session_id but no face_choose and no face_id; face_choose present but falsy (empty list, empty dict).

Common situations: Developer skips reading the identify result's face list; face selection delegated to an LLM step that returned nothing.

Related errors


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