{"record":{"id":"66473767d15847d7","repo":"microsoft/VibeVoice","slug":"unsupported-audio-type-type-audio","errorCode":null,"errorMessage":"Unsupported audio type: {type(audio)}","messagePattern":"Unsupported audio type: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"vibevoice/processor/vibevoice_tokenizer_processor.py","lineNumber":331,"sourceCode":"            raise ImportError(\n                \"soundfile is required to save audio files. \"\n                \"Install it with: pip install soundfile\"\n            )\n        \n        # Ensure audio is in the right format\n        if isinstance(audio, torch.Tensor):\n            # Convert PyTorch tensor to numpy\n            audio_np = audio.float().detach().cpu().numpy()\n        elif isinstance(audio, np.ndarray):\n            audio_np = audio\n        elif isinstance(audio, list):\n            # Handle list of tensors or arrays\n            if all(isinstance(a, torch.Tensor) for a in audio):\n                audio_np = [a.float().detach().cpu().numpy() for a in audio]\n            else:\n                audio_np = audio\n        else:\n            raise ValueError(f\"Unsupported audio type: {type(audio)}\")\n        \n        saved_paths = []\n        \n        # Handle based on shape or type\n        if isinstance(audio_np, list):\n            # Multiple separate audios to save\n            output_dir = output_path\n            \n            # Ensure output directory exists\n            os.makedirs(output_dir, exist_ok=True)\n            \n            # Save each audio\n            for i, audio_item in enumerate(audio_np):\n                audio_item = self._prepare_audio_for_save(audio_item, normalize)\n                file_path = os.path.join(output_dir, f\"{batch_prefix}{i}.wav\")\n                sf.write(file_path, audio_item, sampling_rate)\n                saved_paths.append(file_path)\n                ","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/microsoft/VibeVoice/blob/94da20d98b2fa7688e9cbfaf7692ddb4954f7600/vibevoice/processor/vibevoice_tokenizer_processor.py#L313-L349","documentation":"`save_audio()` only accepts `torch.Tensor`, `np.ndarray`, or a `list` of tensors/arrays (see the Union in its signature at vibevoice/processor/vibevoice_tokenizer_processor.py:284). Any other type falls through to `raise ValueError(f\"Unsupported audio type: {type(audio)}\")` before any file I/O happens. It is an input-contract error raised by explicit type dispatch, not by serialization.","triggerScenarios":"Passing a Python tuple of tensors, a generator, a string path to a wav file, a JAX/TF tensor, or a 0-d/odd object to `save_audio()`. Also passing a nested list of lists, since only a flat list of torch.Tensors is converted element-wise.","commonSituations":"Pipeline code that collects outputs into tuples instead of lists, feeding a model output after `.cpu()` chained onto a tuple, or handing `save_audio` a file path expecting it to read audio (it only writes).","solutions":["Convert the value to a supported type before saving: wrap tuples with `list(...)`, convert non-torch arrays with `torch.as_tensor(...)` or `np.asarray(...)`.","If you meant to load audio, use the processor's audio-loading API (or soundfile/librosa) — `save_audio` only writes.","For mixed lists (tensors + numpy), normalize first: `[a.numpy() if isinstance(a, torch.Tensor) else a for a in audio]`."],"exampleFix":"# before\ntracks = tuple(w.cpu() for w in wavs)  # tuple -> ValueError\nprocessor.save_audio(tracks, \"out_dir\")\n\n# after\ntracks = [w.detach().cpu().numpy() for w in wavs]  # list of arrays\nprocessor.save_audio(tracks, \"out_dir\")","handlingStrategy":"type-guard","validationCode":"import torch, numpy as np\n\ndef as_saveable(audio):\n    if isinstance(audio, (torch.Tensor, np.ndarray)):\n        return audio\n    if isinstance(audio, (list, tuple)):\n        return [a.detach().cpu().numpy() if isinstance(a, torch.Tensor) else a for a in audio]\n    raise TypeError(f\"Cannot save audio of type {type(audio)!r}\")\n\nprocessor.save_audio(as_saveable(wav), \"out.wav\")","typeGuard":"def is_saveable_audio(audio) -> bool:\n    import torch\n    if isinstance(audio, (torch.Tensor, np.ndarray)):\n        return True\n    return isinstance(audio, (list, tuple)) and all(\n        isinstance(a, (torch.Tensor, np.ndarray)) for a in audio\n    )","tryCatchPattern":"try:\n    processor.save_audio(wav, out)\nexcept ValueError as e:\n    if \"Unsupported audio type\" in str(e):\n        wav = list(wav) if isinstance(wav, tuple) else wav\n        processor.save_audio(wav, out)\n    else:\n        raise","preventionTips":["Normalize model outputs to a torch.Tensor or numpy array at the pipeline boundary, before passing to save_audio.","Convert tuples to lists — the API accepts only list for multi-audio.","Remember save_audio writes; never pass a file path as the audio argument."],"tags":["python","audio","type-validation","input-contract"],"backgroundTag":null,"analyzedSha":"94da20d98b2fa7688e9cbfaf7692ddb4954f7600","analyzedAt":"2026-08-15T04:12:07.418Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}