HKUDS/DeepTutor · error · VoiceProviderError

No endpoint URL configured for this provider.

Error message

No endpoint URL configured for this provider.

What it means

join_audio_path(base_url, suffix) builds an audio endpoint URL from an API base. If base_url is empty or whitespace, it cannot construct a URL and raises VoiceProviderError. Full URLs already containing "/audio/" are passed through verbatim with the query string preserved (e.g. Azure-style endpoints).

Source

Thrown at deeptutor/services/voice/base.py:96

    Chrome's ``MediaRecorder.mimeType`` is typically ``audio/webm;codecs=opus``.
    OpenAI-compatible transcription endpoints treat the codec parameter as an
    unknown format and return 400 (``Unsupported file format: ...``). Keep the
    type/subtype only.
    """
    media_type = (content_type or "").split(";", 1)[0].strip()
    return media_type or "application/octet-stream"


def join_audio_path(base_url: str, suffix: str) -> str:
    """Append an OpenAI audio path to a configured base URL.

    ``base_url`` is the API base (e.g. ``https://api.openai.com/v1``). If the
    admin already pasted a full ``.../audio/...`` endpoint (some gateways /
    Azure deployments), it is used verbatim and the query string preserved.
    """
    base = (base_url or "").strip()
    if not base:
        raise VoiceProviderError("No endpoint URL configured for this provider.")
    head, sep, query = base.partition("?")
    if "/audio/" in head:
        return base
    joined = f"{head.rstrip('/')}/{suffix.lstrip('/')}"
    return f"{joined}?{query}" if sep else joined


# Content blocks that should never be spoken aloud, stripped before synthesis.
_FENCED_CODE = re.compile(r"```.*?```", re.DOTALL)
_INLINE_CODE = re.compile(r"`([^`]*)`")
_IMAGE = re.compile(r"!\[[^\]]*\]\([^)]*\)")
_LINK = re.compile(r"\[([^\]]+)\]\([^)]*\)")
_HEADING = re.compile(r"^\s{0,3}#{1,6}\s*", re.MULTILINE)
_BLOCKQUOTE = re.compile(r"^\s{0,3}>\s?", re.MULTILINE)
_LIST_MARKER = re.compile(r"^\s{0,3}(?:[-*+]|\d+[.)])\s+", re.MULTILINE)
_EMPHASIS = re.compile(r"(\*{1,3}|_{1,3}|~~)(\S.*?\S|\S)\1")
_HTML_TAG = re.compile(r"<[^>]+>")
_TABLE_PIPE = re.compile(r"^\s*\|.*\|\s*$", re.MULTILINE)

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Set the provider base_url (e.g. https://api.openai.com/v1) in voice settings
  2. If you already have a full .../audio/... endpoint (Azure), pass it as base_url — it is used verbatim
  3. Validate voice config at startup; any enabled provider must have a non-empty endpoint

Example fix

// before
url = join_audio_path("", "audio/speech")
// after
url = join_audio_path("https://api.openai.com/v1", "audio/speech")
Defensive patterns

Strategy: validation

Validate before calling

from deeptutor.services.voice.base import join_audio_path

base = (config.base_url or "").strip()
if not base:
    raise ValueError("Voice provider endpoint (base_url) is required")
url = join_audio_path(base, "audio/speech")

Type guard

def has_voice_endpoint(config) -> bool:
    return bool((getattr(config, "base_url", None) or "").strip())

Try / catch

try:
    url = join_audio_path(config.base_url, suffix)
except VoiceProviderError as exc:
    if "No endpoint URL" in str(exc):
        raise ConfigurationError("voice provider missing base_url") from exc
    raise

Prevention

When it happens

Trigger: Calling synthesize or transcribe (or the tested helper directly) with an empty base_url — the shared guard used by both TTS and STT URL construction.

Common situations: Missing voice provider endpoint in settings, env override blanking the value, or passing None from a config that was never populated for the selected provider.

Related errors


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