{"record":{"id":"f2f559dea35f72b2","repo":"HKUDS/DeepTutor","slug":"no-endpoint-url-configured-for-this-provider-f2f559","errorCode":null,"errorMessage":"No endpoint URL configured for this provider.","messagePattern":"No endpoint URL configured for this provider\\.","errorType":"exception","errorClass":"VoiceProviderError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/voice/base.py","lineNumber":96,"sourceCode":"    Chrome's ``MediaRecorder.mimeType`` is typically ``audio/webm;codecs=opus``.\n    OpenAI-compatible transcription endpoints treat the codec parameter as an\n    unknown format and return 400 (``Unsupported file format: ...``). Keep the\n    type/subtype only.\n    \"\"\"\n    media_type = (content_type or \"\").split(\";\", 1)[0].strip()\n    return media_type or \"application/octet-stream\"\n\n\ndef join_audio_path(base_url: str, suffix: str) -> str:\n    \"\"\"Append an OpenAI audio path to a configured base URL.\n\n    ``base_url`` is the API base (e.g. ``https://api.openai.com/v1``). If the\n    admin already pasted a full ``.../audio/...`` endpoint (some gateways /\n    Azure deployments), it is used verbatim and the query string preserved.\n    \"\"\"\n    base = (base_url or \"\").strip()\n    if not base:\n        raise VoiceProviderError(\"No endpoint URL configured for this provider.\")\n    head, sep, query = base.partition(\"?\")\n    if \"/audio/\" in head:\n        return base\n    joined = f\"{head.rstrip('/')}/{suffix.lstrip('/')}\"\n    return f\"{joined}?{query}\" if sep else joined\n\n\n# Content blocks that should never be spoken aloud, stripped before synthesis.\n_FENCED_CODE = re.compile(r\"```.*?```\", re.DOTALL)\n_INLINE_CODE = re.compile(r\"`([^`]*)`\")\n_IMAGE = re.compile(r\"!\\[[^\\]]*\\]\\([^)]*\\)\")\n_LINK = re.compile(r\"\\[([^\\]]+)\\]\\([^)]*\\)\")\n_HEADING = re.compile(r\"^\\s{0,3}#{1,6}\\s*\", re.MULTILINE)\n_BLOCKQUOTE = re.compile(r\"^\\s{0,3}>\\s?\", re.MULTILINE)\n_LIST_MARKER = re.compile(r\"^\\s{0,3}(?:[-*+]|\\d+[.)])\\s+\", re.MULTILINE)\n_EMPHASIS = re.compile(r\"(\\*{1,3}|_{1,3}|~~)(\\S.*?\\S|\\S)\\1\")\n_HTML_TAG = re.compile(r\"<[^>]+>\")\n_TABLE_PIPE = re.compile(r\"^\\s*\\|.*\\|\\s*$\", re.MULTILINE)","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/voice/base.py#L78-L114","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set the provider base_url (e.g. https://api.openai.com/v1) in voice settings","If you already have a full .../audio/... endpoint (Azure), pass it as base_url — it is used verbatim","Validate voice config at startup; any enabled provider must have a non-empty endpoint"],"exampleFix":"// before\nurl = join_audio_path(\"\", \"audio/speech\")\n// after\nurl = join_audio_path(\"https://api.openai.com/v1\", \"audio/speech\")","handlingStrategy":"validation","validationCode":"from deeptutor.services.voice.base import join_audio_path\n\nbase = (config.base_url or \"\").strip()\nif not base:\n    raise ValueError(\"Voice provider endpoint (base_url) is required\")\nurl = join_audio_path(base, \"audio/speech\")","typeGuard":"def has_voice_endpoint(config) -> bool:\n    return bool((getattr(config, \"base_url\", None) or \"\").strip())","tryCatchPattern":"try:\n    url = join_audio_path(config.base_url, suffix)\nexcept VoiceProviderError as exc:\n    if \"No endpoint URL\" in str(exc):\n        raise ConfigurationError(\"voice provider missing base_url\") from exc\n    raise","preventionTips":["Pass full /audio/... URLs verbatim for Azure-style deployments","Validate endpoints at settings-save time","Never construct voice configs from partial env vars without defaults"],"tags":["voice","url-building","missing-config","tts","stt"],"backgroundTag":"missing-base-url","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}