odysseus-dev/odysseus · error · HTTPException

Synthesis failed: {str(e)}

Error message

Synthesis failed: {str(e)}

What it means

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.

Source

Thrown at routes/tts_routes.py:72

                        detail={"message": "Synthesis failed"}
                    )
                
                # Detect format from magic bytes (MP3: ID3 tag or sync word ff e0+)
                is_mp3 = audio_data[:3] == b'ID3' or (len(audio_data) >= 2 and audio_data[0] == 0xff and (audio_data[1] & 0xe0) == 0xe0)
                mime = "audio/mpeg" if is_mp3 else "audio/wav"
                return Response(
                    content=audio_data,
                    media_type=mime,
                    headers={
                        "Content-Disposition": "inline; filename=speech.mp3" if "mpeg" in mime else "inline; filename=speech.wav"
                    }
                )
        
        except HTTPException:
            raise
        except Exception as e:
            logger.error(f"Synthesis error: {e}", exc_info=True)
            raise HTTPException(
                status_code=500,
                detail={"message": f"Synthesis failed: {str(e)}"}
            )

    @router.post("/clear-cache")
    async def clear_tts_cache():
        """Clear TTS cache"""
        try:
            tts_service.clear_cache()
            return {"success": True, "message": "Cache cleared"}
        except Exception as e:
            logger.error(f"Failed to clear cache: {e}")
            raise HTTPException(status_code=500, detail=str(e))

    return router

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the server log line 'Synthesis error: ...' with stack trace — the HTTP detail is only the summary
  2. Fix the underlying cause (network reachability, provider auth, text encoding)
  3. Add request timeouts/retries around the provider call inside the service
  4. Return a generic message instead of str(e) if the API is exposed to untrusted clients

Example fix

# before
raise HTTPException(status_code=500, detail={"message": f"Synthesis failed: {str(e)}"})
# after
logger.exception("Synthesis error")
raise HTTPException(status_code=502, detail={"message": "TTS provider error"})
Defensive patterns

Strategy: try-catch

Validate before calling

if (!ttsServiceAvailable()) throw new Error('TTS off'); // avoids most 500 paths

Try / catch

try { await synthesize(text); } catch (e) { if (is5xx(e)) showGenericTtsError(e); /* never surface str(e) verbatim */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/31652182108ce7c8. Report an issue: GitHub.