{"record":{"id":"2be13acdd134891a","repo":"HKUDS/DeepTutor","slug":"openrouter-chat-audio-returned-no-audio-chunks-or","errorCode":null,"errorMessage":"OpenRouter chat audio returned no audio chunks; original /audio/speech error: {original_error}","messagePattern":"OpenRouter chat audio returned no audio chunks; original /audio/speech error: (.+?)","errorType":"exception","errorClass":"VoiceProviderError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/voice/adapters/openai_compat.py","lineNumber":242,"sourceCode":"            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\n\n    @staticmethod\n    def _collect_audio_line(line: str, audio_chunks: list[str]) -> None:\n        if not line:\n            return\n        raw = line.strip()\n        if not raw.startswith(\"data:\"):","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/voice/adapters/openai_compat.py#L224-L260","documentation":"The OpenRouter chat/completions fallback completed with HTTP 200 and its SSE lines were scanned, but no audio chunks were collected (audio_chunks empty). The adapter raises VoiceProviderError including the original /audio/speech error for context. It means the model replied with text (or nothing parseable) instead of audio data.","triggerScenarios":"The chat request succeeded but the model returned plain text content with no base64 audio payload — e.g. the model doesn't support the audio output modulation, or the prompt/modality flags weren't accepted.","commonSituations":"Using a non-audio model on OpenRouter with the fallback enabled; the gateway silently dropping the audio field; or an SSE format change that _collect_audio_line no longer recognizes.","solutions":["Use an audio-capable model (e.g. *-audio-preview) on OpenRouter","Check debug logs for \"Ignoring malformed OpenRouter SSE line\" — a format change would show there","Test the same chat/completions request with curl including the audio output modulation to confirm the model emits audio","Fall back to a native TTS provider for speech synthesis"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    audio, ct = await openrouter_adapter.synthesize(text, config)\nexcept VoiceProviderError as exc:\n    if \"no audio chunks\" in str(exc):\n        return await native_tts.synthesize(text, native_config)\n    raise","preventionTips":["Use audio-preview models for the OpenRouter fallback","Log which model produced no chunks to build a known-good list"],"tags":["voice","tts","openrouter","empty-response","sse"],"backgroundTag":"empty-response-body","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}