{"record":{"id":"39e39532de221bb9","repo":"HKUDS/DeepTutor","slug":"tts-request-error-detail","errorCode":null,"errorMessage":"TTS request error: {detail}","messagePattern":"TTS request error: (.+?)","errorType":"exception","errorClass":"VoiceProviderError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/voice/adapters/openai_compat.py","lineNumber":235,"sourceCode":"\n        logger.debug(\n            \"OpenRouter chat-audio synthesize url=%s model=%s voice=%s fmt=%s chars=%d\",\n            url,\n            config.model,\n            config.voice,\n            audio_format,\n            len(text),\n        )\n        audio_chunks: list[str] = []\n        try:\n            async with httpx.AsyncClient(timeout=config.request_timeout) as client:\n                resp = await client.post(url, headers=headers, json=payload)\n            _raise_for_provider(resp, \"OpenRouter chat audio synthesis\")\n            for line in (resp.text or \"\").splitlines():\n                self._collect_audio_line(line, audio_chunks)\n        except httpx.HTTPError as exc:\n            detail = str(exc) or exc.__class__.__name__\n            raise VoiceProviderError(f\"TTS request error: {detail}\") from exc\n        except VoiceProviderHTTPError as exc:\n            raise VoiceProviderError(\n                f\"{exc}; original /audio/speech error: {original_error}\"\n            ) from exc\n\n        if not audio_chunks:\n            raise VoiceProviderError(\n                \"OpenRouter chat audio returned no audio chunks; \"\n                f\"original /audio/speech error: {original_error}\"\n            )\n        try:\n            audio = base64.b64decode(\"\".join(audio_chunks))\n        except binascii.Error as exc:\n            raise VoiceProviderError(\"OpenRouter chat audio returned invalid base64.\") from exc\n        if not audio:\n            raise VoiceProviderError(\"OpenRouter chat audio returned empty audio.\")\n        content_type = _FORMAT_CONTENT_TYPES.get(audio_format, \"application/octet-stream\")\n        return audio, content_type","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/voice/adapters/openai_compat.py#L217-L253","documentation":"During the OpenRouter chat/completions audio fallback, the httpx POST failed at the transport level (httpx.HTTPError) and the adapter wraps it as VoiceProviderError(\"TTS request error: {detail}\"). Same family as the main-path transport error but on the fallback endpoint.","triggerScenarios":"Network/DNS/TLS/timeout failure specifically while POSTing to {base_url}/chat/completions in the fallback, after /audio/speech already failed with an HTTP error.","commonSituations":"Transient network drop between the two requests, request_timeout too small for chat-with-audio generation (which is slower than direct TTS), or proxy interference on streaming responses.","solutions":["Increase request_timeout — chat audio generation is slower than /audio/speech","Retry the whole synthesis; if /audio/speech fails persistently at HTTP level, fix that first (see the original error chain)","Verify egress to the chat/completions endpoint","Consider disabling the OpenRouter fallback and using a native TTS provider"],"exampleFix":"// before\ncfg = TTSConfig(request_timeout=10)\n// after\ncfg = TTSConfig(request_timeout=120)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    return await adapter.synthesize(text, config)\nexcept VoiceProviderError as exc:\n    if \"TTS request error\" in str(exc):\n        return await asyncio.wait_for(adapter.synthesize(text, config), timeout=180)\n    raise","preventionTips":["Chat-audio generation is slow — budget generous timeouts","Keep a native TTS provider as fallback when OpenRouter is flaky"],"tags":["voice","tts","openrouter","network","httpx"],"backgroundTag":"http-request-failed","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}