HKUDS/DeepTutor · error · VoiceProviderError

OpenRouter chat audio returned empty audio.

Error message

OpenRouter chat audio returned empty audio.

What it means

The OpenRouter fallback successfully decoded base64 audio but the decoded bytes are empty (zero-length). The adapter refuses to return empty audio and raises VoiceProviderError.

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)

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.