calesthio/OpenMontage · error · ValueError

advanced_lip_sync requires at least 2000ms of cropped audio

Error message

advanced_lip_sync requires at least 2000ms of cropped audio

What it means

Raised by KlingLipSyncTool._copy_timing_fields on the advanced lip-sync path when sound_end_time - sound_start_time < 2000 ms. Kling's advanced lip-sync API rejects cropped audio segments shorter than 2 seconds, so the tool enforces the floor before sending the request.

Source

Thrown at tools/avatar/kling_lip_sync.py:563

                value = default
            face_item[key] = int(value)

        nested_end = face_item.get("sound_end_time")
        top_level_end = inputs.get("sound_end_time")
        if (
            nested_end is not None
            and top_level_end is not None
            and int(nested_end) != int(top_level_end)
        ):
            raise ValueError(
                "Conflicting sound_end_time values between top-level input and face_choose[0]"
            )
        sound_end = nested_end if nested_end is not None else top_level_end
        if sound_end is None:
            raise ValueError("advanced_lip_sync requires sound_end_time")
        face_item["sound_end_time"] = int(sound_end)
        if face_item["sound_end_time"] - face_item["sound_start_time"] < 2000:
            raise ValueError("advanced_lip_sync requires at least 2000ms of cropped audio")

        for key in ("sound_volume", "original_audio_volume"):
            nested = face_item.get(key)
            top_level = inputs.get(key)
            if nested is not None and top_level is not None and float(nested) != float(top_level):
                raise ValueError(
                    f"Conflicting {key} values between top-level input and face_choose[0]"
                )
            value = nested if nested is not None else top_level
            if value is not None:
                face_item[key] = float(value)

    @staticmethod
    def _face_choose_result_metadata(
        face_choose: list[dict[str, Any]],
    ) -> list[dict[str, Any]]:
        metadata: list[dict[str, Any]] = []
        for item in face_choose:

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Extend the crop window so end - start >= 2000 ms (pad with silence if the speech is shorter)
  2. Shift sound_start_time closer to sound_end_time if you accidentally left it at 0 while the audio lives later in the file
  3. If the clip genuinely must be <2s, use the non-advanced lip-sync path or pad the audio file itself

Example fix

# before
face = {"sound_start_time": 0, "sound_end_time": 1500}

# after
face = {"sound_start_time": 0, "sound_end_time": 2000}
Defensive patterns

Strategy: validation

Validate before calling

face_item = (inputs.get("face_choose") or [{}])[0]
start = int(face_item.get("sound_start_time") or inputs.get("sound_start_time") or 0)
end = face_item.get("sound_end_time") if face_item.get("sound_end_time") is not None else inputs.get("sound_end_time")
if end is None or int(end) - start < 2000:
    raise ValueError("crop window must be >= 2000ms")

Prevention

When it happens

Trigger: advanced_lip_sync with sound_start_time=0 and sound_end_time=1500; or face_choose[0] = {'sound_start_time': 4000, 'sound_end_time': 5500}. Defaults matter too: if sound_start_time defaults to 0, sound_end_time must be >= 2000.

Common situations: Trying to lip-sync a single short word or a 1-second SFX cue; computing the crop window from a transcript where a spoken phrase lasted under 2s.

Related errors


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