{"record":{"id":"605a937633163cca","repo":"calesthio/OpenMontage","slug":"face-choose-items-must-include-face-id","errorCode":null,"errorMessage":"face_choose items must include face_id","messagePattern":"face_choose items must include face_id","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/avatar/kling_lip_sync.py","lineNumber":404,"sourceCode":"            \"model\": \"kling-official-lip-sync\",\n            \"audio_source\": audio_source,\n        }\n\n    @staticmethod\n    def _normalize_face_choose(inputs: dict[str, Any]) -> list[dict[str, Any]]:\n        if inputs.get(\"face_choose\"):\n            raw = inputs[\"face_choose\"]\n            if isinstance(raw, dict):\n                raw = [raw]\n            if not isinstance(raw, list):\n                raise ValueError(\"face_choose must be a list of face choice objects\")\n            normalized: list[dict[str, Any]] = []\n            for item in raw:\n                if isinstance(item, str):\n                    normalized.append({\"face_id\": item})\n                elif isinstance(item, dict):\n                    if not (item.get(\"face_id\") or item.get(\"id\")):\n                        raise ValueError(\"face_choose items must include face_id\")\n                    record = dict(item)\n                    if \"face_id\" not in record and record.get(\"id\"):\n                        record[\"face_id\"] = record.pop(\"id\")\n                    normalized.append(record)\n                else:\n                    raise ValueError(\"face_choose items must be strings or objects\")\n            return normalized\n        if inputs.get(\"face_id\"):\n            return [{\"face_id\": str(inputs[\"face_id\"])}]\n        return []\n\n    def _face_selection(\n        self,\n        faces: list[dict[str, Any]],\n        inputs: dict[str, Any],\n    ) -> tuple[list[dict[str, Any]], dict[str, Any]]:\n        explicit = self._normalize_face_choose(inputs)\n        if explicit:","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/avatar/kling_lip_sync.py#L386-L422","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add the identifier: {'face_id': '1', ...} — use the face_id values from the identify_face result.","If the source records use 'id', that is accepted too and normalized automatically.","Bare strings in the list also work: ['1']."],"exampleFix":"// before\nresult = tool.run({..., \"face_choose\": [{\"face_index\": 0, \"lb\": 1.0}]})\n\n// after\nresult = tool.run({..., \"face_choose\": [{\"face_id\": \"1\", \"face_index\": 0, \"lb\": 1.0}]})","handlingStrategy":"validation","validationCode":"for item in face_choose:\n    if isinstance(item, dict):\n        assert item.get(\"face_id\") or item.get(\"id\"), \\\n            f\"face_choose item missing face_id: {item}\"","typeGuard":"def item_has_face_id(item: dict) -> bool:\n    return bool(item.get(\"face_id\") or item.get(\"id\"))","tryCatchPattern":null,"preventionTips":["Copy face_id values verbatim from the identify_face result","Remember 'id' is accepted and normalized to 'face_id'; other keys alone are not"],"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"}