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
- Retry with a shorter, clearly speakable text input
- Switch to an audio-capable model or a native TTS provider
- Check the original /audio/speech error (not included here) for the underlying provider issue
- 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
- Keep a secondary TTS provider configured for automatic failover
- Smoke-test new models with short utterances before production use
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
- OpenRouter chat audio returned no audio chunks; original…
- {exc}{hint}
- ; original /audio/speech error
- No endpoint URL configured for this provider.
- OpenRouter chat audio error
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])
returnView on GitHub (pinned to 3e82f13042)