calesthio/OpenMontage · error · ValueError
full_lip_sync could not infer sound_end_time; provide it exp
Error message
full_lip_sync could not infer sound_end_time; provide it explicitly
What it means
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.
Source
Thrown at tools/avatar/kling_lip_sync.py:516
face_start = int(face.get("start_time") or 0)
face_end = int(face.get("end_time") or 0)
face_choose = self._normalize_face_choose(inputs)
face_item = face_choose[0] if face_choose else {}
if inputs.get("sound_start_time") is None and face_item.get("sound_start_time") is None:
inputs["sound_start_time"] = 0
if inputs.get("sound_insert_time") is None and face_item.get("sound_insert_time") is None:
inputs["sound_insert_time"] = face_start
if inputs.get("sound_end_time") is not None or face_item.get("sound_end_time") is not None:
return
candidates: list[int] = []
audio_duration = self._local_audio_duration_ms(inputs)
if audio_duration:
candidates.append(audio_duration)
if face_end > face_start:
candidates.append(face_end - face_start)
if not candidates:
raise ValueError(
"full_lip_sync could not infer sound_end_time; provide it explicitly"
)
inputs["sound_end_time"] = min(candidates)
@staticmethod
def _local_audio_duration_ms(inputs: dict[str, Any]) -> int | None:
sound_path = inputs.get("sound_file_path") or inputs.get("audio_path")
if not sound_path:
return None
path = Path(sound_path)
if not path.is_file():
return None
seconds = probe_output(path).get("duration_seconds")
if not seconds:
return None
return int(round(float(seconds) * 1000))
@staticmethodView on GitHub (pinned to 95e1c3d0ab)
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
Example fix
# before
inputs = {"video_id": vid, "sound_file_url": url, "face_choose": [{"face_id": fid}]}
# after
inputs = {"video_id": vid, "sound_file_url": url, "face_choose": [{"face_id": fid}], "sound_end_time": 8000} Defensive patterns
Strategy: validation
Validate before calling
import os
needs_end = inputs.get("sound_end_time") is None and not (inputs.get("face_choose") or [{}])[0].get("sound_end_time")
probeable = os.path.isfile(inputs.get("sound_file_path") or inputs.get("audio_path") or "")
if needs_end and not probeable:
raise ValueError("sound_end_time required: audio is remote/unprobeable") Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Conflicting {key} values between top-level input and face_ch
- Conflicting sound_end_time values between top-level input an
- advanced_lip_sync requires sound_end_time
- advanced_lip_sync requires at least 2000ms of cropped audio
- Unsupported Kling lip-sync operation: {operation}
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/fe5e06c8d240caa8.
Report an issue: GitHub.