HKUDS/DeepTutor · error · VoiceProviderError

OpenRouter chat audio returned empty audio.

Error message

OpenRouter chat audio returned empty audio.

What it means

The OpenRouter chat-completions audio path completed but yielded no audio chunks (audio_chunks empty). The message also embeds the original /audio/speech error that caused the fallback to chat audio, so the root cause is usually that earlier TTS failure.

Solutions

  1. Retry with a shorter, clearly speakable text input
  2. Switch to an audio-capable model or a native TTS provider
  3. Check the original /audio/speech error (not included here) for the underlying provider issue
  4. Enable debug logging to see the raw chunks received
Defensive patterns

Strategy: fallback

Try / catch

try:
    audio, ct = await adapter.synthesize(text, config)
except VoiceProviderError as exc:
    if "empty audio" in str(exc):
        return await native_tts.synthesize(text, native_config)
    raise

Prevention

When it happens

Trigger: The SSE stream contained one or more chunks that decode to zero bytes — e.g. a single empty-string chunk, or padding-only base64.

Common situations: Model acknowledged the audio request but produced an empty payload (often when the prompt elicits no spoken content, or the modulation is ignored). Usually paired with a prior /audio/speech failure.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/306ba1c6ac0854b9. Report an issue: GitHub.

Appendix: source

Thrown at deeptutor/services/voice/adapters/openai_compat.py:251

        except httpx.HTTPError as exc:
            detail = str(exc) or exc.__class__.__name__
            raise VoiceProviderError(f"TTS request error: {detail}") from exc
        except VoiceProviderHTTPError as exc:
            raise VoiceProviderError(
                f"{exc}; original /audio/speech error: {original_error}"
            ) from exc

        if not audio_chunks:
            raise VoiceProviderError(
                "OpenRouter chat audio returned no audio chunks; "
                f"original /audio/speech error: {original_error}"
            )
        try:
            audio = base64.b64decode("".join(audio_chunks))
        except binascii.Error as exc:
            raise VoiceProviderError("OpenRouter chat audio returned invalid base64.") from exc
        if not audio:
            raise VoiceProviderError("OpenRouter chat audio returned empty audio.")
        content_type = _FORMAT_CONTENT_TYPES.get(audio_format, "application/octet-stream")
        return audio, content_type

    @staticmethod
    def _collect_audio_line(line: str, audio_chunks: list[str]) -> None:
        if not line:
            return
        raw = line.strip()
        if not raw.startswith("data:"):
            return
        data = raw[len("data:") :].strip()
        if not data or data == "[DONE]":
            return
        try:
            chunk = json.loads(data)
        except json.JSONDecodeError:
            logger.debug("Ignoring malformed OpenRouter SSE line: %s", data[:160])
            return

View on GitHub (pinned to 3e82f13042)