odysseus-dev/odysseus · error · HTTPException

str(e)

Error message

str(e)

What it means

HTTP 500 from GET /api/tts/stats raised when tts_service.get_stats() throws any exception; the handler logs it and re-raises with detail=str(e), forwarding the raw internal message to the client. Typical underlying causes are an uninitialized TTS backend or corrupt cache statistics.

Source

Thrown at routes/tts_routes.py:28

logger = logging.getLogger(__name__)

class TTSRequest(BaseModel):
    text: str
    format: str = "audio"  # "audio" or "base64"

def setup_tts_routes(tts_service):
    """Setup TTS routes with the provided TTS service"""
    router = APIRouter(prefix="/api/tts", tags=["tts"])

    @router.get("/stats")
    async def get_tts_stats():
        """Get TTS service statistics"""
        try:
            return tts_service.get_stats()
        except Exception as e:
            logger.error(f"Failed to get TTS stats: {e}")
            raise HTTPException(status_code=500, detail=str(e))

    @router.post("/synthesize")
    async def synthesize_speech(request: TTSRequest):
        """Synthesize speech from text"""
        try:
            if not tts_service.available:
                raise HTTPException(
                    status_code=503,
                    detail={"message": "TTS service not available"}
                )
            
            if request.format == "base64":
                audio_b64 = tts_service.synthesize_to_base64(request.text)
                if not audio_b64:
                    raise HTTPException(
                        status_code=500,
                        detail={"message": "Synthesis failed"}
                    )

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check the server logs for the original 'Failed to get TTS stats' line to identify the real exception
  2. Verify the TTS backend dependency is installed and tts_service.available is true
  3. If stats are only broken while the backend is down, fix availability first — this 500 is a symptom
  4. Patch the route to return a generic 500 message instead of str(e) to avoid detail leakage

Example fix

# before
raise HTTPException(status_code=500, detail=str(e))
# after
raise HTTPException(status_code=500, detail="TTS stats unavailable")
Defensive patterns

Strategy: try-catch

Validate before calling

const stats = await getTtsStats();
if (!stats.available) { disableTtsUi(); }

Try / catch

try { const s = await getTtsStats(); } catch (e) { /* 500: log, disable TTS UI, do not retry-loop */ }

Prevention

When it happens

Trigger: GET /api/tts/stats when the TTS service failed during startup (missing edge-tts/pyttsx3 dependency, no network to the TTS provider) and get_stats() dereferences an uninitialized backend.

Common situations: Deployed without the TTS extras installed; TTS provider endpoint unreachable so partial init left stats in a broken state; str(e) in the response leaking internal paths/exception text to clients.

Related errors


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