{"record":{"id":"75574c2e13866846","repo":"ATH-MaaS/Pixelle-Video","slug":"invalid-dashscope-media-combination-join-sor","errorCode":null,"errorMessage":"Invalid DashScope media combination: {'+'.join(sorted(media_types))}. Allowed: first_frame, first_frame+driving_audio, first_frame+last_frame, first_frame+last_frame+driving_audio, first_clip, first_clip+last_frame.","messagePattern":"Invalid DashScope media combination: (.+?)\\. Allowed: first_frame, first_frame\\+driving_audio, first_frame\\+last_frame, first_frame\\+last_frame\\+driving_audio, first_clip, first_clip\\+last_frame\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pixelle_video/services/api_services/video_dashscope.py","lineNumber":556,"sourceCode":"        for ref_video_path in reference_video_paths or []:\n            if ref_video_path:\n                media.append({\"type\": \"reference_video\", \"url\": self._to_media_url(ref_video_path)})\n\n        return media\n\n    def _validate_media_combination(self, media: list[dict[str, str]]) -> None:\n        \"\"\"Validate combinations documented by DashScope wan2.7 i2v.\"\"\"\n        media_types = {item[\"type\"] for item in media}\n        allowed = [\n            {\"first_frame\"},\n            {\"first_frame\", \"driving_audio\"},\n            {\"first_frame\", \"last_frame\"},\n            {\"first_frame\", \"last_frame\", \"driving_audio\"},\n            {\"first_clip\"},\n            {\"first_clip\", \"last_frame\"},\n        ]\n        if media_types not in allowed:\n            raise ValueError(\n                \"Invalid DashScope media combination: \"\n                f\"{'+'.join(sorted(media_types))}. \"\n                \"Allowed: first_frame, first_frame+driving_audio, first_frame+last_frame, \"\n                \"first_frame+last_frame+driving_audio, first_clip, first_clip+last_frame.\"\n            )\n\n    def _to_media_url(self, path_or_url: str) -> str:\n        \"\"\"Convert a local path to file:// while preserving URL/data/OSS inputs.\"\"\"\n        if path_or_url.startswith((\"http://\", \"https://\", \"file://\", \"oss://\", \"data:\")):\n            return path_or_url\n        return f\"file://{os.path.abspath(path_or_url)}\"\n\n\nif __name__ == \"__main__\":\n    import sys\n    import time\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n    from config import Config","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/ATH-MaaS/Pixelle-Video/blob/848b054e4fae40dabc62ec58e960b573e83793ac/pixelle_video/services/api_services/video_dashscope.py#L538-L574","documentation":"_validate_media_combination checks that the set of media inputs passed to a DashScope wan2.7+ video generation is one of the whitelisted combinations. Any set of media types not exactly matching an allowed combination raises ValueError before the API call is made.","triggerScenarios":"Calling generate_video with, e.g., only {'last_frame'} (no first_frame), {'first_clip','driving_audio'} (audio without clips' frames), {'first_frame','first_clip'} together, or {'driving_audio'} alone — the media set built by the caller doesn't match any allowed set (video_dashscope.py:552-558).","commonSituations":"Passing last_frame without first_frame, supplying driving_audio to an image-only workflow, accidentally including both an image and a video clip, or a refactored caller building the media dict with a typo in the media type key ('firstframe', 'audio').","solutions":["Change the media inputs to one of the allowed combos: first_frame; first_frame+driving_audio; first_frame+last_frame; first_frame+last_frame+driving_audio; first_clip; first_clip+last_frame.","If you only have an end frame, add a first_frame or switch to first_clip.","Move driving_audio off a clip-only call — audio is only accepted with first_frame inputs.","Fix typos/keys in the media dict; the error prints the offending combination as '+'.join(sorted(media_types)) to compare directly."],"exampleFix":"# before\nmedia = {\"last_frame\": end_image_url}\nself._validate_media_combination(media)  # ValueError\n# after\nmedia = {\"first_frame\": start_image_url, \"last_frame\": end_image_url}\nself._validate_media_combination(media)  # OK","handlingStrategy":"validation","validationCode":"ALLOWED = [\n    {\"first_frame\"}, {\"first_frame\", \"driving_audio\"},\n    {\"first_frame\", \"last_frame\"},\n    {\"first_frame\", \"last_frame\", \"driving_audio\"},\n    {\"first_clip\"}, {\"first_clip\", \"last_frame\"},\n]\nmedia_types = set(media.keys())\nif media_types not in ALLOWED:\n    raise ValueError(f\"不支持的媒体组合: {sorted(media_types)}\")","typeGuard":"def is_valid_media_combo(media: dict) -> bool:\n    allowed = [\n        {\"first_frame\"}, {\"first_frame\", \"driving_audio\"},\n        {\"first_frame\", \"last_frame\"},\n        {\"first_frame\", \"last_frame\", \"driving_audio\"},\n        {\"first_clip\"}, {\"first_clip\", \"last_frame\"},\n    ]\n    return set(media.keys()) in allowed","tryCatchPattern":"try:\n    url = client.generate_video(media=media, ...)\nexcept ValueError as e:\n    if \"Invalid DashScope media combination\" in str(e):\n        media = {\"first_frame\": media.get(\"first_frame\") or media.get(\"last_frame\")}\n        url = client.generate_video(media=media, ...)","preventionTips":["Build media dicts from a fixed helper that only emits allowed combinations","Never send driving_audio with first_clip inputs","Provide first_frame whenever you provide last_frame","Unit-test media combination builders against the whitelist"],"tags":["validation","input-validation","dashscope","argument-error"],"backgroundTag":"invalid-input-combination","analyzedSha":"848b054e4fae40dabc62ec58e960b573e83793ac","analyzedAt":"2026-08-30T03:24:41.468Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}