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

  1. Always pass a voice supported by the model: litellm.speech(model="openai/gpt-4o-audio-preview", input="...", voice="alloy").
  2. 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.
  3. If calling the handler directly, include voice so transform_request emits modalities + audio params.
  4. 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

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


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)