SubtitleEdit/subtitleedit · error · InvalidOperationException
Kokoro TTS synthesis failed ({(int)response.StatusCode}): {e
Error message
Kokoro TTS synthesis failed ({(int)response.StatusCode}): {errorBody} What it means
InvalidOperationException from KokoroTtsCpp.Speak when the POST to `{ServerBaseUrl}/v1/synthesize` returns a non-success status. The numeric code and the response body (SafeReadErrorAsync) are embedded, and the full diagnostic (voice, text, body) is logged via Se.LogError. The server stays running; this is a per-request synthesis error.
Source
Thrown at src/ui/Features/Video/TextToSpeech/Engines/KokoroTtsCpp.cs:254
await EnsureServerRunningAsync(cancellationToken);
var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetFolder), Guid.NewGuid() + ".wav");
var inputText = text;
var voiceName = string.IsNullOrEmpty(kokoroVoice.Voice) ? DefaultVoice : kokoroVoice.Voice;
var body = JsonSerializer.Serialize(new { text = inputText, voice = voiceName });
using var content = new StringContent(body, Encoding.UTF8, "application/json");
Se.WriteToolsLog($"Kokoro TTS: POST {ServerBaseUrl}/v1/synthesize (voice={voiceName}, textLen={text.Length})");
using var response = await HttpClient.PostAsync($"{ServerBaseUrl}/v1/synthesize", content, cancellationToken);
if (!response.IsSuccessStatusCode)
{
var errorBody = await SafeReadErrorAsync(response, cancellationToken);
var errMsg = $"Kokoro TTS server error {(int)response.StatusCode} {response.StatusCode} - "
+ $"Voice: {voiceName}, Text: {text}, Body: {errorBody}";
Se.LogError(errMsg);
Se.WriteToolsLog(errMsg);
throw new InvalidOperationException(
$"Kokoro TTS synthesis failed ({(int)response.StatusCode}): {errorBody}");
}
await using (var fileStream = File.Create(outputFileName))
await using (var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken))
{
await contentStream.CopyToAsync(fileStream, cancellationToken);
}
return new TtsResult(outputFileName, text);
}
private static async Task<string> SafeReadErrorAsync(HttpResponseMessage response, CancellationToken ct)
{
try
{
return await response.Content.ReadAsStringAsync(ct);
}View on GitHub (pinned to 17a9f07487)
Solutions
- Read the `(status): errorBody` — kokoro's server returns a JSON error message.
- HTTP 400 on voice → confirm the voice name exists in the voices model (call GetVoices first).
- Ensure input text is non-empty after trimming.
- Verify the kokoro-tts-server version matches the `/v1/synthesize` contract the code expects.
- If the DefaultVoice constant is stale, update it to a voice present in the shipped voices file.
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate voice name against the loaded voices model before POST
var valid = (await kokoroEngine.GetVoices(language)).Select(v => v.Name).ToHashSet();
if (!valid.Contains(voiceName)) { WarnUser("Voice not in voices file"); return; } Try / catch
try { await kokoroEngine.Speak(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Kokoro TTS synthesis failed"))
{
var status = ExtractStatusCode(ex.Message);
if (status == 400) WarnUser("Bad voice/text — check voices file and non-empty text.");
else if (status >= 500) { await Task.Delay(500, ct); await kokoroEngine.Speak(...); }
} Prevention
- Call GetVoices() and validate the chosen voice name before synthesis.
- Trim and reject empty input text before POSTing.
- Keep DefaultVoice in sync with the shipped voices file.
- Pin the kokoro-tts-server version matching the `/v1/synthesize` contract.
When it happens
Trigger: HTTP 400 from an unknown/empty voice name; 500 from an internal kokoro inference error; 422 from input the model can't handle (empty text, unsupported characters); the voices file passed at startup doesn't contain the requested voice.
Common situations: Voice name in the request not present in the loaded voices model file; text is empty or only whitespace; server version drift changed the `/v1/synthesize` schema; the DefaultVoice fallback fired but isn't in the voices file.
Related errors
- IndexTTS (CrispASR) synthesis failed ({(int)response.StatusC
- CosyVoice3 (CrispASR) — the crispasr server crashed during s
- CosyVoice3 (CrispASR) synthesis failed ({(int)response.Statu
- F5-TTS (CrispASR) — the crispasr server crashed during synth
- F5-TTS (CrispASR) synthesis failed ({(int)response.StatusCod
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/9da59bbade8b58a1.
Report an issue: GitHub.