{"record":{"id":"8678123aa362f785","repo":"calesthio/OpenMontage","slug":"advanced-lip-sync-requires-face-choose-or-face-id","errorCode":null,"errorMessage":"advanced_lip_sync requires face_choose or face_id","messagePattern":"advanced_lip_sync requires face_choose or face_id","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/avatar/kling_lip_sync.py","lineNumber":370,"sourceCode":"        if inputs.get(\"video_id\"):\n            payload[\"video_id\"] = str(inputs[\"video_id\"])\n        if inputs.get(\"video_url\"):\n            payload[\"video_url\"] = str(inputs[\"video_url\"])\n        if not payload:\n            raise ValueError(\"Kling identify_face requires video_id or video_url\")\n        return {\n            \"path\": \"/v1/videos/identify-face\",\n            \"payload\": payload,\n            \"operation\": \"identify_face\",\n        }\n\n    def _build_advanced_request(self, inputs: dict[str, Any]) -> dict[str, Any]:\n        session_id = str(inputs.get(\"session_id\") or \"\").strip()\n        if not session_id:\n            raise ValueError(\"advanced_lip_sync requires session_id\")\n        face_choose = self._normalize_face_choose(inputs)\n        if not face_choose:\n            raise ValueError(\"advanced_lip_sync requires face_choose or face_id\")\n        if len(face_choose) != 1:\n            raise ValueError(\"advanced_lip_sync currently supports exactly one face_choose item\")\n        face_item = face_choose[0]\n        audio_source = self._copy_audio_input(inputs, face_item)\n        self._copy_timing_fields(inputs, face_item)\n        payload: dict[str, Any] = {\n            \"session_id\": session_id,\n            \"face_choose\": face_choose,\n        }\n        self._copy_common_task_fields(inputs, payload)\n        return {\n            \"protocol\": \"classic\",\n            \"path\": \"/v1/videos/advanced-lip-sync\",\n            \"payload\": payload,\n            \"operation\": \"advanced_lip_sync\",\n            \"model\": \"kling-official-lip-sync\",\n            \"audio_source\": audio_source,\n        }","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/avatar/kling_lip_sync.py#L352-L388","documentation":"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.","triggerScenarios":"Calling advanced_lip_sync with session_id but no face_choose and no face_id; face_choose present but falsy (empty list, empty dict).","commonSituations":"Developer skips reading the identify result's face list; face selection delegated to an LLM step that returned nothing.","solutions":["Read the faces from the identify_face result (or the faces artifact file) and pass one as face_id.","Simplest form: face_id='1' for the first detected face.","Full form: face_choose=[{'face_id': '1', ...timing fields...}]."],"exampleFix":"// before\nresult = tool.run({\"operation\": \"advanced_lip_sync\", \"session_id\": sid, \"audio_path\": \"a.mp3\"})\n\n// after\nresult = tool.run({\"operation\": \"advanced_lip_sync\", \"session_id\": sid, \"face_id\": \"1\", \"audio_path\": \"a.mp3\"})","handlingStrategy":"validation","validationCode":"assert inputs.get(\"face_choose\") or inputs.get(\"face_id\"), \\\n    \"advanced_lip_sync needs a face selection from the identify result\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Read the identify result's face list and pass one detected face_id","Default to face_id='1' when the caller has no selection logic"],"tags":["kling","lip-sync","required-field","face-selection"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}