{"record":{"id":"9da59bbade8b58a1","repo":"SubtitleEdit/subtitleedit","slug":"kokoro-tts-synthesis-failed-int-response-status","errorCode":null,"errorMessage":"Kokoro TTS synthesis failed ({(int)response.StatusCode}): {errorBody}","messagePattern":"Kokoro TTS synthesis failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/KokoroTtsCpp.cs","lineNumber":254,"sourceCode":"        await EnsureServerRunningAsync(cancellationToken);\n\n        var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetFolder), Guid.NewGuid() + \".wav\");\n        var inputText = text;\n        var voiceName = string.IsNullOrEmpty(kokoroVoice.Voice) ? DefaultVoice : kokoroVoice.Voice;\n\n        var body = JsonSerializer.Serialize(new { text = inputText, voice = voiceName });\n        using var content = new StringContent(body, Encoding.UTF8, \"application/json\");\n        Se.WriteToolsLog($\"Kokoro TTS: POST {ServerBaseUrl}/v1/synthesize (voice={voiceName}, textLen={text.Length})\");\n        using var response = await HttpClient.PostAsync($\"{ServerBaseUrl}/v1/synthesize\", content, cancellationToken);\n\n        if (!response.IsSuccessStatusCode)\n        {\n            var errorBody = await SafeReadErrorAsync(response, cancellationToken);\n            var errMsg = $\"Kokoro TTS server error {(int)response.StatusCode} {response.StatusCode} - \"\n                + $\"Voice: {voiceName}, Text: {text}, Body: {errorBody}\";\n            Se.LogError(errMsg);\n            Se.WriteToolsLog(errMsg);\n            throw new InvalidOperationException(\n                $\"Kokoro TTS synthesis failed ({(int)response.StatusCode}): {errorBody}\");\n        }\n\n        await using (var fileStream = File.Create(outputFileName))\n        await using (var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken))\n        {\n            await contentStream.CopyToAsync(fileStream, cancellationToken);\n        }\n\n        return new TtsResult(outputFileName, text);\n    }\n\n    private static async Task<string> SafeReadErrorAsync(HttpResponseMessage response, CancellationToken ct)\n    {\n        try\n        {\n            return await response.Content.ReadAsStringAsync(ct);\n        }","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/KokoroTtsCpp.cs#L236-L272","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Validate voice name against the loaded voices model before POST\nvar valid = (await kokoroEngine.GetVoices(language)).Select(v => v.Name).ToHashSet();\nif (!valid.Contains(voiceName)) { WarnUser(\"Voice not in voices file\"); return; }","typeGuard":null,"tryCatchPattern":"try { await kokoroEngine.Speak(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Kokoro TTS synthesis failed\"))\n{\n    var status = ExtractStatusCode(ex.Message);\n    if (status == 400) WarnUser(\"Bad voice/text — check voices file and non-empty text.\");\n    else if (status >= 500) { await Task.Delay(500, ct); await kokoroEngine.Speak(...); }\n}","preventionTips":["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."],"tags":["kokoro","http-status","synthesis","tts"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}