{"record":{"id":"43a44112f6ceafa9","repo":"sgl-project/sglang","slug":"whisper-expects-exactly-1-audio-input-got-len","errorCode":null,"errorMessage":"Whisper expects exactly 1 audio input, got {len}","messagePattern":"Whisper expects exactly 1 audio input, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/whisper.py","lineNumber":178,"sourceCode":"                f\"Language '{language}' is not in this Whisper model's vocabulary. \"\n                f\"The '{language_token}' token may have been added in a later \"\n                f\"Whisper version than the loaded checkpoint.\"\n            )\n        return token_id\n\n    async def process_mm_data_async(\n        self,\n        image_data,\n        audio_data,\n        input_text,\n        request_obj,\n        **kwargs,\n    ) -> Optional[Dict[str, Any]]:\n        if not audio_data:\n            return None\n\n        if len(audio_data) != 1:\n            raise ValueError(\n                f\"Whisper expects exactly 1 audio input, got {len(audio_data)}\"\n            )\n\n        # Check if this is a fused auto-detect request (decoder prompt = [SOT] only,\n        # structured generation handles the rest via regex constraint).\n        detect_language = self._pop_sampling_param(request_obj, FUSED_AUTODETECT_FLAG)\n        # timestamp_granularities is a transcription-level field; it must be\n        # popped in both branches or it leaks into SamplingParams(**kwargs)\n        # downstream and TypeErrors. In the fused branch the FSM regex was\n        # already picked in build_fused_autodetect_params based on this value,\n        # so we only need to keep it here to pick the timestamp_token_id for\n        # the explicit-language branch.\n        timestamp_granularities = self._pop_sampling_param(\n            request_obj, \"timestamp_granularities\"\n        )\n\n        audios = [load_audio(audio) for audio in audio_data]\n","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/whisper.py#L160-L196","documentation":"WhisperProcessor.process_mm_data_async handles strictly one audio input per request. audio_data must be truthy (else it returns None) and of length exactly 1; any batch of 2+ audio clips raises ValueError. Whisper's decoder is built around a single utterance per forward.","triggerScenarios":"Sending a multimodal request whose audio_data list contains 2 or more audio entries (multiple clips, chunked audio segments, or a mistranslated 'audio' array field).","commonSituations":"Splitting long recordings into chunks and submitting them together; frontend serializing a single audio as a list of segments; batching APIs reused for audio.","solutions":["Submit one audio clip per request and loop client-side","Concatenate clips into a single waveform before sending if they form one utterance","Fix request serialization so one clip is one element, not a nested list"],"exampleFix":"# before\nresp = client.generate(prompt, audio_data=[clip1, clip2])\n# after\nfor clip in [clip1, clip2]:\n    resp = client.generate(prompt, audio_data=[clip])","handlingStrategy":"validation","validationCode":"if not audio_data or len(audio_data) != 1:\n    raise ValueError('exactly one audio input required')","typeGuard":"def is_single_audio(req) -> bool:\n    a = req.get('audio_data') or req.get('audio')\n    return isinstance(a, list) and len(a) == 1","tryCatchPattern":null,"preventionTips":["Enforce one-clip-per-request in the client SDK","Concatenate chunked audio into one waveform before submission"],"tags":["whisper","audio","single-input-constraint"],"backgroundTag":"unsupported-input-cardinality","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}