{"record":{"id":"271468abe3d94290","repo":"sgl-project/sglang","slug":"expected-batch-size-1-for-decoded-audio-got-shape","errorCode":null,"errorMessage":"Expected batch size 1 for decoded audio, got shape={tuple(waveform.shape)}","messagePattern":"Expected batch size 1 for decoded audio, got shape=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/joy_echo/memory.py","lineNumber":540,"sourceCode":"    if video_uint8.shape[-1] != 3:\n        raise ValueError(\n            f\"Expected RGB video with trailing channel dim 3, got shape={tuple(video_uint8.shape)}\"\n        )\n    video_uint8 = video_uint8.detach().cpu().contiguous()\n    return [Image.fromarray(frame.numpy()) for frame in video_uint8]\n\n\ndef normalize_audio_waveform_for_media(\n    audio_waveform: Optional[torch.Tensor],\n) -> Optional[torch.Tensor]:\n    if audio_waveform is None:\n        return None\n\n    waveform = torch.as_tensor(audio_waveform).detach().cpu().float()\n\n    if waveform.ndim == 3:\n        if waveform.shape[0] != 1:\n            raise ValueError(\n                f\"Expected batch size 1 for decoded audio, got shape={tuple(waveform.shape)}\"\n            )\n        waveform = waveform[0]\n    if waveform.ndim == 1:\n        waveform = waveform.unsqueeze(0)\n    elif (\n        waveform.ndim == 2\n        and waveform.shape[0] not in {1, 2}\n        and waveform.shape[1]\n        in {\n            1,\n            2,\n        }\n    ):\n        waveform = waveform.transpose(0, 1)\n    elif waveform.ndim != 2:\n        raise ValueError(\n            f\"Expected decoded audio with 1, 2, or 3 dims, got shape={tuple(waveform.shape)}\"","sourceCodeStart":522,"sourceCodeEnd":558,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/joy_echo/memory.py#L522-L558","documentation":"When a decoded audio waveform arrives with 3 dimensions [B, T, C], the batch dimension must be 1 because the Joy-Echo memory slot stores a single clip; a multi-clip batch cannot be saved into one memory slot. The waveform is then indexed [0] to drop the batch dim.","triggerScenarios":"Calling normalize_audio_waveform_for_media (via save_memory_slot) with a [B,T,C] tensor where B>1, e.g. a batched codec/VAE decoder output of multiple audio clips.","commonSituations":"Feeding batched audio-decoder output directly instead of indexing the clip you want; batch inference where each sample's audio must be saved to its own memory slot.","solutions":["Index the desired clip before passing: waveform = waveform[i] or waveform[i:i+1]","If generating one slot per batch item, loop over the batch and call save_memory_slot per item with audio_waveform=batch[i:i+1]","Add an assert on waveform.shape[0]==1 in your own wrapper to fail with context"],"exampleFix":"# before\nsave_memory_slot(..., audio_waveform=batched_waveform)  # [4, T, C]\n# after\nfor i in range(batched_waveform.shape[0]):\n    save_memory_slot(..., audio_waveform=batched_waveform[i:i+1])","handlingStrategy":"validation","validationCode":"if waveform.ndim == 3 and waveform.shape[0] != 1:\n    raise ValueError(f\"select a single clip before saving; batch dim is {waveform.shape[0]}\")","typeGuard":"def is_single_clip_waveform(t: torch.Tensor) -> bool:\n    return t.ndim in (1, 2) or (t.ndim == 3 and t.shape[0] == 1)","tryCatchPattern":null,"preventionTips":["Index batched decoder output per item before building memory slots","Keep per-sample memory-slot creation inside the batch loop"],"tags":["joy-echo","audio","waveform","batching","memory-slot"],"backgroundTag":"invalid-tensor-shape","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}