calesthio/OpenMontage · error · ValueError
Conflicting audio input in face_choose[0]; provide audio_id
Error message
Conflicting audio input in face_choose[0]; provide audio_id or sound_file, not both
What it means
Raised by KlingLipSyncTool._copy_audio_input when face_choose[0] contains both audio_id and sound_file. The Kling payload accepts exactly one audio source per face; supplying both an uploaded-audio reference and inline audio data is ambiguous, so the tool refuses before building the request.
Source
Thrown at tools/avatar/kling_lip_sync.py:593
@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:
record = {key: value for key, value in item.items() if key != "sound_file"}
if item.get("sound_file"):
record["sound_file_provided"] = True
metadata.append(record)
return metadata
@staticmethod
def _copy_audio_input(inputs: dict[str, Any], payload: dict[str, Any]) -> dict[str, Any]:
nested_audio_id = str(payload.get("audio_id") or "").strip()
nested_sound_file = payload.get("sound_file")
if nested_audio_id and nested_sound_file:
raise ValueError(
"Conflicting audio input in face_choose[0]; provide audio_id or sound_file, not both"
)
top_level_audio_id = str(inputs.get("audio_id") or "").strip()
top_level_sound_requested = any(
inputs.get(key)
for key in ("sound_file", "sound_file_url", "sound_file_path", "audio_path")
)
if nested_audio_id:
if (
(top_level_audio_id and top_level_audio_id != nested_audio_id)
or top_level_sound_requested
):
raise ValueError(
"Conflicting audio input between top-level fields and face_choose[0]"
)
payload["audio_id"] = nested_audio_idView on GitHub (pinned to 95e1c3d0ab)
Solutions
- Delete one of the two fields from face_choose[0] — keep audio_id if you pre-uploaded the audio, otherwise keep sound_file
- If you have a file, upload it first (or let the tool handle sound_file) and drop audio_id
- Validate face_choose entries for mutual exclusivity of audio_id/sound_file before calling
Example fix
# before
face = {"face_id": "f1", "audio_id": "audio_123", "sound_file": "https://cdn/vo.mp3"}
# after
face = {"face_id": "f1", "sound_file": "https://cdn/vo.mp3"} Defensive patterns
Strategy: validation
Validate before calling
face_item = (inputs.get("face_choose") or [{}])[0]
if str(face_item.get("audio_id") or "").strip() and face_item.get("sound_file"):
raise ValueError("face_choose[0] has both audio_id and sound_file") Type guard
def has_single_audio_source(face_item) -> bool:
return bool(str(face_item.get("audio_id") or "").strip()) != bool(face_item.get("sound_file")) Prevention
- Choose one audio representation per face: uploaded id or file/URL
- When migrating fields, delete the old one
- Validate mutual exclusivity before the call
When it happens
Trigger: face_choose=[{'face_id': 'f1', 'audio_id': 'audio_123', 'sound_file': 'https://.../vo.mp3'}] on the advanced path. Either non-empty audio_id (after strip) together with a truthy sound_file triggers it.
Common situations: Migrating a payload from sound_file to a pre-uploaded audio_id and leaving the old field behind; agent templates merging every audio option into one dict.
Related errors
- Conflicting audio input between top-level fields and face_ch
- Conflicting {key} values between top-level input and face_ch
- Conflicting sound_end_time values between top-level input an
- advanced_lip_sync requires audio_id, sound_file, sound_file_
- Unsupported Kling lip-sync operation: {operation}
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/9ac429442d37de9f.
Report an issue: GitHub.