HKUDS/DeepTutor · error · VoiceProviderError

No audio data to transcribe.

Error message

No audio data to transcribe.

What it means

transcribe rejects empty audio input before making any request: if the bytes passed are empty (or a falsy value), VoiceProviderError("No audio data to transcribe.") is raised. This prevents pointless provider round-trips and confusing downstream errors.

Source

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

class OpenAICompatSTTAdapter(BaseSTTAdapter):
    """POST ``{base}/audio/transcriptions``.

    Multipart ``file`` upload by default; OpenRouter uses a base64-JSON body
    (``request_style == "base64_json"``) sharing the same path.
    """

    async def transcribe(
        self,
        audio: bytes,
        config: STTConfig,
        *,
        filename: str = "audio.webm",
        content_type: str = "application/octet-stream",
    ) -> str:
        if not config.base_url:
            raise VoiceProviderError("No endpoint URL configured for STT.")
        if not audio:
            raise VoiceProviderError("No audio data to transcribe.")
        url = join_audio_path(config.base_url, "audio/transcriptions")
        auth = build_auth_headers(config.auth_style, config.api_key)

        try:
            async with httpx.AsyncClient(timeout=config.request_timeout) as client:
                if config.request_style == STT_BASE64_JSON:
                    resp = await self._post_base64(client, url, auth, audio, filename, config)
                else:
                    resp = await self._post_multipart(
                        client, url, auth, audio, filename, content_type, config
                    )
        except httpx.HTTPError as exc:
            raise VoiceProviderError(f"STT request error: {exc}") from exc
        _raise_for_provider(resp, "Transcription")
        return self._parse_text(resp)

    async def _post_multipart(
        self,

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Check audio length before calling transcribe; skip or prompt the user to re-record
  2. Fix the upstream capture path if recordings are systematically empty (permissions, codec, timing)
  3. In tests, use a real small audio fixture

Example fix

// before
if audio:
    pass
text = await stt.transcribe(audio, cfg)
// after
if not audio:
    return ""
text = await stt.transcribe(audio, cfg)
Defensive patterns

Strategy: validation

Validate before calling

if not audio:
    return ""  # or prompt user to re-record
text = await stt.transcribe(audio, stt_config)

Type guard

def has_audio_payload(audio: bytes | None) -> bool:
    return bool(audio)

Prevention

When it happens

Trigger: Calling transcribe(b"", config) or transcribe with an empty buffer — e.g. a zero-length recording, a failed client-side capture, or an upload that produced no bytes.

Common situations: Browser mic permission granted but recording stopped instantly, WebSocket audio frame aggregation producing an empty blob, or a test passing an empty fixture.

Related errors


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