odysseus-dev/odysseus · error · Error

Synthesis failed

Error message

Synthesis failed

What it means

Fallback error when server-side TTS preview fails without a structured detail message. The code POSTs {text, format:'audio'} to /api/tts/synthesize; on non-OK it tries to read err.detail?.message and falls back to 'Synthesis failed' when the body isn't the expected JSON shape.

Source

Thrown at static/js/settings.js:944

            var target = voiceVal.toLowerCase();
            var match = voices.find(function(v) { return v.name.toLowerCase() === target; }) ||
                        voices.find(function(v) { return v.name.toLowerCase().includes(target); });
            if (match) utt.voice = match;
          }
          utt.rate = parseFloat(speedSelect.value) || 1;
          previewBtn.textContent = 'Stop'; previewBtn.style.borderColor = 'var(--red, #e55)';
          await new Promise(function(resolve, reject) {
            utt.onend = resolve;
            utt.onerror = function(e) { reject(new Error('Browser TTS: ' + e.error)); };
            window.speechSynthesis.speak(utt);
          });
        } else {
          var res = await fetch('/api/tts/synthesize', {
            method: 'POST', credentials: 'same-origin',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ text: testText, format: 'audio' })
          });
          if (!res.ok) { var err = await res.json().catch(function() { return {}; }); throw new Error(err.detail?.message || 'Synthesis failed'); }
          var blob = await res.blob();
          var url = URL.createObjectURL(blob);
          previewAudio = new Audio(url);
          previewBtn.textContent = 'Stop'; previewBtn.style.borderColor = 'var(--red, #e55)';
          await new Promise(function(resolve, reject) {
            previewAudio.onended = function() { URL.revokeObjectURL(url); previewAudio = null; resolve(); };
            previewAudio.onerror = function() { URL.revokeObjectURL(url); previewAudio = null; reject(new Error('Playback failed')); };
            previewAudio.play().catch(reject);
          });
        }
      } catch (e) {
        ttsMsg.textContent = 'Preview failed: ' + e.message; ttsMsg.style.color = 'var(--red, #e55)';
        setTimeout(function() { ttsMsg.textContent = ''; }, 3000);
      } finally {
        resetPreview();
      }
    });
  }

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check the response status and raw body of the failing request in devtools
  2. Verify the TTS provider credentials/config on the server
  3. If a proxy returns HTML/empty bodies, fix the proxy or make the client accept text bodies
  4. Improve the fallback to include res.status so users see which failure occurred

Example fix

// before
          if (!res.ok) { var err = await res.json().catch(function() { return {}; }); throw new Error(err.detail?.message || 'Synthesis failed'); }

// after
          if (!res.ok) {
            var err = await res.json().catch(function() { return {}; });
            throw new Error(err.detail?.message || 'Synthesis failed (HTTP ' + res.status + ')');
          }
Defensive patterns

Strategy: try-catch

Validate before calling

if (!prov || prov === 'disabled') return;
if (!navigator.onLine) { ttsMsg.textContent = 'Offline'; return; }

Try / catch

try { const res = await fetch('/api/tts/synthesize', {...}); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.detail?.message || `Synthesis failed (HTTP ${res.status})`); } ... } catch (e) { ttsMsg.textContent = 'Preview failed: ' + e.message; }

Prevention

When it happens

Trigger: POST /api/tts/synthesize returns non-2xx with an empty or differently-shaped body (plain-text 500 traceback, HTML error page from a proxy, 502/504 from a gateway) — err.detail?.message is undefined so the generic message is used.

Common situations: TTS provider (e.g. ElevenLabs/other) API key missing or invalid; provider quota exhausted; reverse proxy intercepting the request; backend TTS subsystem not configured; network layer returning HTML error pages.

Related errors


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