BerriAI/litellm · error · ValueError
No audio part found in the response
Error message
No audio part found in the response
What it means
SpeechToCompletionBridgeTransformationHandler.transform_response (transformation.py:98) expects choices[0].message.audio to be present on the ModelResponse and raises ValueError when it is None. The audio part is only produced when the underlying chat model actually returned audio — the request must have carried modalities=["audio"] AND the audio voice/format parameter. Note transform_request only adds the audio param when voice is not None (transformation.py:28-32), so a speech call without voice can yield a plain text response with no audio part.
Solutions
- Always pass a voice supported by the model: litellm.speech(model="openai/gpt-4o-audio-preview", input="...", voice="alloy").
- Use an audio-capable model: OpenAI gpt-4o-audio-preview / gpt-4o-mini-audio-preview, or Gemini TTS models (e.g. gemini-2.5-flash-preview-tts), which the bridge auto-converts from PCM16 to WAV.
- If calling the handler directly, include voice so transform_request emits modalities + audio params.
- Inspect the raw completion for the same input to confirm the provider returns message.audio before blaming the bridge.
Example fix
# before: no voice -> no audio param -> provider returns text-only -> ValueError
audio = litellm.speech(model="openai/gpt-4o-audio-preview", input="Hello world")
# after: voice supplied, audio modality negotiated
audio = litellm.speech(
model="openai/gpt-4o-audio-preview",
input="Hello world",
voice="alloy",
response_format={"format": "wav"},
) Defensive patterns
Strategy: try-catch
Validate before calling
AUDIO_CAPABLE = ("gpt-4o-audio-preview", "gpt-4o-mini-audio-preview", "tts") # 'tts' catches gemini *-tts models
def assert_audio_capable(model: str, voice: str | None) -> None:
if voice is None:
raise ValueError("speech bridge only returns audio when a voice is passed")
if not any(m in model for m in AUDIO_CAPABLE):
raise ValueError(f"{model!r} cannot return audio; use an audio-capable model (gpt-4o-audio-preview, gemini *-tts)") Try / catch
try:
audio = litellm.speech(model=model, input=text, voice=voice)
except ValueError as e:
if "No audio part" in str(e):
raise RuntimeError(
f"{model!r} returned no audio — pass voice= and use an audio-capable model "
"(gpt-4o-audio-preview / gemini *-tts)"
) from e
raise Prevention
- Always pass voice when using the speech bridge — without it the audio request param is never sent.
- Restrict speech routes to models that support the audio modality.
- Smoke-test new TTS models with a one-off completion call and assert message.audio is present before wiring them in.
When it happens
Trigger: Calling litellm.speech()/the bridge without voice (no audio param sent, provider returns text-only); using a model that does not support audio output (modalities=["audio"] ignored by the provider); a provider bug/drop of the audio field in the response; response_format mismatches causing the provider to omit audio.
Common situations: Assuming any chat model can TTS via the bridge (e.g. plain gpt-4o instead of gpt-4o-audio-preview or a gemini *-tts model); omitting the voice argument because it looks optional; providers partially implementing the audio modality.
Related errors
- custom_llm_provider is required
- headers is required
- input is required
- litellm_params is required
- logging_obj is required
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/f1d7367d72ef2fff.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/endpoints/speech/speech_to_completion_bridge/transformation.py:108
)
return wav_header + pcm_data
def _is_gemini_tts_model(self, model: str) -> bool:
"""Check if the model is a Gemini TTS model that returns PCM16 data."""
return "gemini" in model.lower() and ("tts" in model.lower() or "preview-tts" in model.lower())
def transform_response(self, model_response: "ModelResponse") -> "HttpxBinaryResponseContent":
import base64
import httpx
from litellm.types.llms.openai import HttpxBinaryResponseContent
from litellm.types.utils import Choices
audio_part: Final = cast(Choices, model_response.choices[0]).message.audio
if audio_part is None:
raise ValueError("No audio part found in the response")
audio_content: Final = audio_part.data
# Decode base64 to get binary content
binary_data = base64.b64decode(audio_content)
# Check if this is a Gemini TTS model that returns raw PCM16 data
model: Final = getattr(model_response, "model", "")
headers: Final = {}
if self._is_gemini_tts_model(model):
# Convert PCM16 to WAV format for proper audio file playback
binary_data = self._convert_pcm16_to_wav(binary_data)
headers["Content-Type"] = "audio/wav"
else:
headers["Content-Type"] = "audio/mpeg"
# Create an httpx.Response object
response: Final = httpx.Response(status_code=200, content=binary_data, headers=headers)
return HttpxBinaryResponseContent(response)View on GitHub (pinned to 77b7c6c40c)