{"record":{"id":"c713e451c29ec3ba","repo":"sgl-project/sglang","slug":"dots-omni-audio-must-be-mono-got-shape-tuple-wav","errorCode":null,"errorMessage":"Dots omni audio must be mono, got shape={tuple(waveform.shape)}","messagePattern":"Dots omni audio must be mono, got shape=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/dots_note_omni.py","lineNumber":245,"sourceCode":"        token_ids = processor.encode(token, add_special_tokens=False)\n        if len(token_ids) != 1:\n            raise ValueError(\n                f\"Dots omni special token {token!r} must encode to one id, got \"\n                f\"{token_ids}\"\n            )\n        return token_ids[0]\n\n    @staticmethod\n    def _normalize_audio(audio) -> torch.Tensor:\n        if isinstance(audio, torch.Tensor):\n            waveform = audio\n        elif isinstance(audio, np.ndarray):\n            waveform = torch.from_numpy(audio)\n        else:\n            waveform = torch.as_tensor(audio)\n        waveform = waveform.float().squeeze()\n        if waveform.ndim != 1:\n            raise ValueError(\n                f\"Dots omni audio must be mono, got shape={tuple(waveform.shape)}\"\n            )\n        return waveform.contiguous()\n\n    def _render_video_content(\n        self,\n        input_text: str,\n        question: str,\n        video_index: int,\n        content: list[dict],\n    ) -> tuple[str, dict[str, tuple[Modality, str]]]:\n        \"\"\"Insert one expanded video while retaining its media ordering.\"\"\"\n        rendered = []\n        media = {}\n        for item in content:\n            item_type = item.get(\"type\")\n            if item_type == \"text\":\n                rendered.append(item.get(\"text\", \"\"))","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/dots_note_omni.py#L227-L263","documentation":"Raised by _normalize_audio when, after squeezing, the waveform tensor is not 1-D — i.e. the audio is stereo/multi-channel rather than mono. The Dots Omni pipeline only accepts mono waveforms.","triggerScenarios":"Sending audio_data (or video-attached audio converted to waveform) whose array has 2+ channels, e.g. shape (2, N); after .squeeze() it stays 2-D, tripping the ndim != 1 check.","commonSituations":"Users feed stereo WAV/PCM arrays or tensors with a channel dimension; many datasets and TTS outputs default to stereo. Note a shape like (1, N) squeezes to (N,) and passes — only genuinely multi-channel audio fails.","solutions":["Downmix to mono before sending: waveform.mean(axis=channel_axis) or use torchaudio/ffmpeg to convert","If passing a tensor/array, ensure final shape is (num_samples,) or (1, num_samples)","Resample is fine — only channel count matters here"],"exampleFix":"# before\naudio = stereo_waveform  # shape (2, N)\n# after\naudio = stereo_waveform.mean(axis=0)  # shape (N,) mono","handlingStrategy":"validation","validationCode":"import numpy as np\ndef to_mono(w):\n    a = np.asarray(w)\n    if a.ndim == 1:\n        return a\n    if a.ndim == 2 and a.shape[0] <= 8:\n        return a.mean(axis=0)\n    raise ValueError(f'unsupported audio shape {a.shape}')\naudio = to_mono(audio)  # send (N,) mono","typeGuard":"def is_mono(audio) -> bool:\n    import numpy as np\n    a = np.asarray(audio)\n    return a.ndim == 1 or (a.ndim == 2 and 1 in a.shape)","tryCatchPattern":null,"preventionTips":["Downmix stereo sources with ffmpeg/-ac 1 at ingest","Verify waveform.shape is (N,) or (1, N) before attaching"],"tags":["multimodal","audio","mono","valueerror"],"backgroundTag":"unsupported-audio-format","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}