decolua/9router · error · Error

Self-hosted TTS failed: ${res.status}

Error message

Self-hosted TTS failed: ${res.status}

What it means

Thrown when a self-hosted OpenAI-compatible TTS server (e.g. local Kokoro/Faster-Whisper style /v1/audio/speech) returns a non-2xx status. The handler prefers a JSON error.message from the body, falling back to this status-code template. It reflects your own server's rejection, not a cloud provider.

Source

Thrown at open-sse/handlers/ttsProviders/selfhostedTts.js:64

      }
    }

    const res = await fetch(`${base}/v1/audio/speech`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        ...(credentials?.apiKey ? { Authorization: `Bearer ${credentials.apiKey}` } : {}),
      },
      body: JSON.stringify({
        model: ttsModel,
        voice,
        input: text,
        response_format: responseFormat,
      }),
    });
    if (!res.ok) {
      const err = await res.json().catch(() => ({}));
      throw new Error(err?.error?.message || `Self-hosted TTS failed: ${res.status}`);
    }
    const buf = await res.arrayBuffer();
    return { base64: Buffer.from(buf).toString("base64"), format: responseFormat };
  },
};

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Check the HTTP status: 404 -> wrong base URL/path, 503 -> server still loading or down, 401 -> auth required
  2. Confirm the TTS server is running and the model is loaded (hit its health/docs endpoint)
  3. Verify base URL, port, and API version path in the 9Router provider config
  4. Request a response_format the server supports (e.g. mp3/wav)
  5. Inspect the self-hosted server logs for the underlying error
Defensive patterns

Strategy: retry

Validate before calling

const health = await fetch(`${baseUrl}/v1/models`).catch(() => null);
if (!health || !health.ok) throw new Error(`Self-hosted TTS unreachable at ${baseUrl}`);

Type guard

function isSelfHostedHttpError(e) {
  return e instanceof Error && /^Self-hosted TTS failed: \d{3}$/.test(e.message);
}

Try / catch

try {
  return await selfHostedTts.synthesize(text, model, opts);
} catch (e) {
  const m = e.message.match(/Self-hosted TTS failed: (\d{3})/);
  if (m && m[1] === "503") return retryWithBackoff(() => selfHostedTts.synthesize(text, model, opts)); // server warming up
  throw e;
}

Prevention

When it happens

Trigger: POST to the configured self-hosted base URL fails: server not fully started, wrong port/path, model not loaded on the server, unsupported response_format requested, or an auth middleware rejecting the request.

Common situations: Docker container still loading the model (503), reverse proxy returning 404/502, requesting a response_format the server doesn't implement, or firewall/local network issues producing 5xx from a proxy.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/dd49714b9a5bb225. Report an issue: GitHub.