{"record":{"id":"fe5e06c8d240caa8","repo":"calesthio/OpenMontage","slug":"full-lip-sync-could-not-infer-sound-end-time-prov","errorCode":null,"errorMessage":"full_lip_sync could not infer sound_end_time; provide it explicitly","messagePattern":"full_lip_sync could not infer sound_end_time; provide it explicitly","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/avatar/kling_lip_sync.py","lineNumber":516,"sourceCode":"        face_start = int(face.get(\"start_time\") or 0)\n        face_end = int(face.get(\"end_time\") or 0)\n        face_choose = self._normalize_face_choose(inputs)\n        face_item = face_choose[0] if face_choose else {}\n        if inputs.get(\"sound_start_time\") is None and face_item.get(\"sound_start_time\") is None:\n            inputs[\"sound_start_time\"] = 0\n        if inputs.get(\"sound_insert_time\") is None and face_item.get(\"sound_insert_time\") is None:\n            inputs[\"sound_insert_time\"] = face_start\n        if inputs.get(\"sound_end_time\") is not None or face_item.get(\"sound_end_time\") is not None:\n            return\n\n        candidates: list[int] = []\n        audio_duration = self._local_audio_duration_ms(inputs)\n        if audio_duration:\n            candidates.append(audio_duration)\n        if face_end > face_start:\n            candidates.append(face_end - face_start)\n        if not candidates:\n            raise ValueError(\n                \"full_lip_sync could not infer sound_end_time; provide it explicitly\"\n            )\n        inputs[\"sound_end_time\"] = min(candidates)\n\n    @staticmethod\n    def _local_audio_duration_ms(inputs: dict[str, Any]) -> int | None:\n        sound_path = inputs.get(\"sound_file_path\") or inputs.get(\"audio_path\")\n        if not sound_path:\n            return None\n        path = Path(sound_path)\n        if not path.is_file():\n            return None\n        seconds = probe_output(path).get(\"duration_seconds\")\n        if not seconds:\n            return None\n        return int(round(float(seconds) * 1000))\n\n    @staticmethod","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/avatar/kling_lip_sync.py#L498-L534","documentation":"Raised by KlingLipSyncTool._apply_face_timing_defaults during full lip-sync when sound_end_time was not supplied at either the top level or in face_choose[0], and the tool cannot infer it. Inference candidates are the local audio file's probed duration (ffprobe on sound_file_path/audio_path) and the detected face's time window (end_time - start_time); if both are unavailable the crop end is unknown.","triggerScenarios":"full_lip_sync with sound_file_url (remote audio, not probe-able locally) on a face record whose end_time <= start_time, and no explicit sound_end_time anywhere. Also when sound_file_path points to a missing file or a file ffprobe cannot read (probe returns no duration_seconds).","commonSituations":"Using URL-based audio with a short or zero-length detected face segment; passing a relative audio path from the wrong working directory; corrupted audio file.","solutions":["Pass sound_end_time explicitly (milliseconds): inputs['sound_end_time'] = 8000","Or put sound_end_time in the face_choose[0] object","If using a local audio file, verify the path exists and ffprobe can read it so duration inference works","Check the identify_face result has a sane end_time > start_time"],"exampleFix":"# before\ninputs = {\"video_id\": vid, \"sound_file_url\": url, \"face_choose\": [{\"face_id\": fid}]}\n\n# after\ninputs = {\"video_id\": vid, \"sound_file_url\": url, \"face_choose\": [{\"face_id\": fid}], \"sound_end_time\": 8000}","handlingStrategy":"validation","validationCode":"import os\nneeds_end = inputs.get(\"sound_end_time\") is None and not (inputs.get(\"face_choose\") or [{}])[0].get(\"sound_end_time\")\nprobeable = os.path.isfile(inputs.get(\"sound_file_path\") or inputs.get(\"audio_path\") or \"\")\nif needs_end and not probeable:\n    raise ValueError(\"sound_end_time required: audio is remote/unprobeable\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass sound_end_time when audio is a URL","Verify local audio paths exist so duration inference can work","Keep ffprobe installed so probe_output can read durations"],"tags":["kling","lip-sync","audio-duration","timing"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}