odysseus-dev/odysseus · warning · Error

Browser TTS not supported

Error message

Browser TTS not supported

What it means

Thrown when the user clicks TTS preview with provider 'browser' but window.speechSynthesis is absent. It is a hard feature-detection failure, not a network or config error — the browser genuinely lacks the Web Speech API.

Source

Thrown at static/js/settings.js:921

    var previewPlaying = false;
    function resetPreview() { previewPlaying = false; previewBtn.textContent = 'Preview'; previewBtn.style.borderColor = ''; }

    previewBtn.addEventListener('click', async function() {
      if (previewPlaying) {
        if (previewAudio) { previewAudio.pause(); previewAudio = null; }
        window.speechSynthesis.cancel();
        resetPreview(); return;
      }
      var prov = provSel.value;
      if (prov === 'disabled') {
        ttsMsg.textContent = 'Select a provider first'; ttsMsg.style.color = 'var(--red, #e55)';
        setTimeout(function() { ttsMsg.textContent = ''; }, 2000); return;
      }
      var testText = 'Hello, this is a test of text to speech.';
      previewPlaying = true; previewBtn.textContent = 'Loading...';
      try {
        if (prov === 'browser') {
          if (!('speechSynthesis' in window)) throw new Error('Browser TTS not supported');
          var utt = new SpeechSynthesisUtterance(testText);
          var voiceVal = getVoice();
          if (voiceVal) {
            var voices = window.speechSynthesis.getVoices();
            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', {

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Hide or disable the 'browser' provider option when 'speechSynthesis' in window is false, before the user can pick it
  2. Fall back to the server-side /api/tts/synthesize provider for these browsers
  3. Detect once at settings-render time and show a hint instead of failing at preview time
  4. Example guard: if (!('speechSynthesis' in window)) disableOption()

Example fix

// before
          if (!('speechSynthesis' in window)) throw new Error('Browser TTS not supported');

// after
          if (!('speechSynthesis' in window)) {
            ttsMsg.textContent = 'Browser TTS not supported — use a server TTS provider';
            ttsMsg.style.color = 'var(--red, #e55)';
            return;
          }
Defensive patterns

Strategy: validation

Validate before calling

const browserTtsAvailable = 'speechSynthesis' in window && typeof window.speechSynthesis.speak === 'function';
if (!browserTtsAvailable) { /* disable 'browser' provider option in provSel */ }

Type guard

function supportsBrowserTts() {
  return typeof window !== 'undefined' &&
    'speechSynthesis' in window &&
    typeof window.speechSynthesis.speak === 'function';
}

Try / catch

try { if (!supportsBrowserTts()) { showHint('Browser TTS unsupported — pick a server provider'); return; } ... } catch (e) { ttsMsg.textContent = 'Preview failed: ' + e.message; }

Prevention

When it happens

Trigger: Selecting the 'browser' TTS provider and clicking preview in any browser without speechSynthesis: older/NIghtly builds, some WebViews/embedded browsers (Electron without the flag, kiosk WebViews), or speechSynthesis removed at runtime.

Common situations: App loaded inside an embedded WebView; user on an exotic or locked-down browser; CI/screenshot environments; the API exists but getVoices() returns empty on some platforms (related but distinct failure).

Related errors


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