{"record":{"id":"31652182108ce7c8","repo":"odysseus-dev/odysseus","slug":"synthesis-failed-str-e","errorCode":null,"errorMessage":"Synthesis failed: {str(e)}","messagePattern":"Synthesis failed: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/tts_routes.py","lineNumber":72,"sourceCode":"                        detail={\"message\": \"Synthesis failed\"}\n                    )\n                \n                # Detect format from magic bytes (MP3: ID3 tag or sync word ff e0+)\n                is_mp3 = audio_data[:3] == b'ID3' or (len(audio_data) >= 2 and audio_data[0] == 0xff and (audio_data[1] & 0xe0) == 0xe0)\n                mime = \"audio/mpeg\" if is_mp3 else \"audio/wav\"\n                return Response(\n                    content=audio_data,\n                    media_type=mime,\n                    headers={\n                        \"Content-Disposition\": \"inline; filename=speech.mp3\" if \"mpeg\" in mime else \"inline; filename=speech.wav\"\n                    }\n                )\n        \n        except HTTPException:\n            raise\n        except Exception as e:\n            logger.error(f\"Synthesis error: {e}\", exc_info=True)\n            raise HTTPException(\n                status_code=500,\n                detail={\"message\": f\"Synthesis failed: {str(e)}\"}\n            )\n\n    @router.post(\"/clear-cache\")\n    async def clear_tts_cache():\n        \"\"\"Clear TTS cache\"\"\"\n        try:\n            tts_service.clear_cache()\n            return {\"success\": True, \"message\": \"Cache cleared\"}\n        except Exception as e:\n            logger.error(f\"Failed to clear cache: {e}\")\n            raise HTTPException(status_code=500, detail=str(e))\n\n    return router\n","sourceCodeStart":54,"sourceCodeEnd":88,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/tts_routes.py#L54-L88","documentation":"HTTP 500 from POST /api/tts/synthesize when any unexpected exception escapes the try block (HTTPExceptions are re-raised untouched by 'except HTTPException: raise'). The catch-all logs with exc_info and returns detail {'message': f'Synthesis failed: {str(e)}'}, embedding the underlying exception text in the response.","triggerScenarios":"Any exception in synthesize_to_base64/synthesize other than the empty-return case: network timeout to the TTS provider, encoding error on exotic input text, AttributeError from a half-initialized backend.","commonSituations":"Provider timeout or DNS failure at synthesis time; Unicode text that breaks the engine's encoder; str(e) exposing internal details (URLs, paths) to API clients.","solutions":["Read the server log line 'Synthesis error: ...' with stack trace — the HTTP detail is only the summary","Fix the underlying cause (network reachability, provider auth, text encoding)","Add request timeouts/retries around the provider call inside the service","Return a generic message instead of str(e) if the API is exposed to untrusted clients"],"exampleFix":"# before\nraise HTTPException(status_code=500, detail={\"message\": f\"Synthesis failed: {str(e)}\"})\n# after\nlogger.exception(\"Synthesis error\")\nraise HTTPException(status_code=502, detail={\"message\": \"TTS provider error\"})","handlingStrategy":"try-catch","validationCode":"if (!ttsServiceAvailable()) throw new Error('TTS off'); // avoids most 500 paths","typeGuard":null,"tryCatchPattern":"try { await synthesize(text); } catch (e) { if (is5xx(e)) showGenericTtsError(e); /* never surface str(e) verbatim */ }","preventionTips":["The server-side stack trace is authoritative — the client sees only the summary","Add a client-side timeout plus a single retry with backoff","Report the server log excerpt when filing issues"],"tags":["http","tts","error-handling","synthesis"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}