{"record":{"id":"53be8210310519ce","repo":"HKUDS/DeepTutor","slug":"exc-hint","errorCode":null,"errorMessage":"{exc}{hint}","messagePattern":"\\{exc\\}\\{hint\\}","errorType":"exception","errorClass":"VoiceProviderError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/voice/adapters/openai_compat.py","lineNumber":153,"sourceCode":"        logger.debug(\n            \"TTS synthesize url=%s model=%s voice=%s fmt=%s chars=%d\",\n            url,\n            config.model,\n            config.voice,\n            response_format,\n            len(text),\n        )\n        try:\n            async with httpx.AsyncClient(timeout=config.request_timeout) as client:\n                resp = await client.post(url, headers=headers, json=payload)\n        except httpx.HTTPError as exc:\n            raise VoiceProviderError(f\"TTS request error: {exc}\") from exc\n        try:\n            _raise_for_provider(resp, \"TTS synthesis\")\n        except VoiceProviderHTTPError as exc:\n            hint = _openrouter_tts_hint(config)\n            if hint:\n                raise VoiceProviderError(f\"{exc}{hint}\") from exc\n            raise\n        audio = resp.content\n        if not audio:\n            raise VoiceProviderError(\"TTS provider returned empty audio.\")\n        content_type = resp.headers.get(\"content-type\") or _FORMAT_CONTENT_TYPES.get(\n            response_format, \"application/octet-stream\"\n        )\n        # Some gateways return JSON content-type with audio; trust the format map.\n        if \"json\" in content_type:\n            content_type = _FORMAT_CONTENT_TYPES.get(response_format, \"audio/mpeg\")\n        return audio, content_type\n\n\nclass OpenRouterTTSAdapter(BaseTTSAdapter):\n    \"\"\"OpenRouter TTS with fallback for streaming chat-audio models.\n\n    OpenRouter documents both a dedicated ``/audio/speech`` endpoint for TTS\n    models and audio output through ``/chat/completions`` for models that expose","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/voice/adapters/openai_compat.py#L135-L171","documentation":"After the provider returned an HTTP >= 400 for TTS synthesis, _raise_for_provider raised VoiceProviderHTTPError; if the config looks like OpenRouter (per _openrouter_tts_hint), the adapter re-raises as VoiceProviderError with an appended hint explaining OpenRouter's audio support. The message combines the provider error detail and the hint.","triggerScenarios":"OpenRouter-style TTS config gets a 4xx/5xx from /audio/speech (e.g. model does not support audio output) and the OpenRouter hint applies, producing this combined message.","commonSituations":"Using an OpenRouter model that lacks native audio/speech support; the hint typically tells you to rely on the chat/completions audio fallback or pick an audio-capable model.","solutions":["Read the appended hint — it usually says to use an audio-capable model or the chat fallback","Switch the TTS model to one that supports /audio/speech on OpenRouter","Verify the API key and account credits if the status is 401/402","If the fallback should have run, ensure the adapter used is OpenRouterTTSAdapter so _synthesize_chat_audio is attempted"],"exampleFix":"// before\ncfg = TTSConfig(model=\"gpt-4o-mini\", base_url=\"https://openrouter.ai/api/v1\")\n// after\ncfg = TTSConfig(model=\"openai/gpt-4o-audio-preview\", base_url=\"https://openrouter.ai/api/v1\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    audio, ct = await adapter.synthesize(text, config)\nexcept VoiceProviderError as exc:\n    if \"OpenRouter\" in str(exc):\n        switch_to_native_tts_provider()  # fallback path\n    raise","preventionTips":["Use audio-capable OpenRouter models for speech","Test the model once after configuring it — a quick smoke synthesis catches unsupported-audio issues early"],"tags":["voice","tts","openrouter","http-error"],"backgroundTag":"provider-http-error","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}