{"record":{"id":"86368a2d60a1403f","repo":"SubtitleEdit/subtitleedit","slug":"indextts-crispasr-synthesis-failed-int-respon","errorCode":null,"errorMessage":"IndexTTS (CrispASR) synthesis failed ({(int)response.StatusCode}): {errorBody}","messagePattern":"IndexTTS \\(CrispASR\\) synthesis failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/IndexTtsCrispAsr.cs","lineNumber":446,"sourceCode":"                + (string.IsNullOrEmpty(serverLog) ? string.Empty : $\"{Environment.NewLine}Server log:{Environment.NewLine}{serverLog}\")\n                + LaunchCmdSuffix(launchCommand),\n                ex);\n        }\n\n        using (response)\n        {\n            if (!response.IsSuccessStatusCode)\n            {\n                var errorBody = await SafeReadErrorAsync(response, cancellationToken);\n                var serverLog = SnapshotServerLog();\n                var launchCommand = _serverLaunchCommand;\n                var errMsg = $\"IndexTTS (CrispASR) server error {(int)response.StatusCode} {response.StatusCode} — \"\n                    + $\"Voice: {indexVoice}, Text: {text}, RequestJson: {body}, \"\n                    + $\"ResponseBody: {errorBody}, ServerLog: {serverLog}\"\n                    + LaunchCmdSuffix(launchCommand);\n                Se.LogError(errMsg);\n                Se.WriteToolsLog(errMsg);\n                throw new InvalidOperationException(\n                    $\"IndexTTS (CrispASR) synthesis failed ({(int)response.StatusCode}): {errorBody}\"\n                    + (string.IsNullOrEmpty(serverLog) ? string.Empty : $\"{Environment.NewLine}Server log:{Environment.NewLine}{serverLog}\")\n                    + LaunchCmdSuffix(launchCommand));\n            }\n\n            await using var fileStream = File.Create(outputFileName);\n            await using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);\n            await contentStream.CopyToAsync(fileStream, cancellationToken);\n        }\n\n        return new TtsResult(outputFileName, text);\n    }\n\n    private static string FormatLaunchCommand(string exe, System.Collections.ObjectModel.Collection<string> args)\n    {\n        static string Quote(string s) =>\n            !string.IsNullOrEmpty(s) && s.IndexOfAny(new[] { ' ', '\\t' }) >= 0\n                ? \"\\\"\" + s.Replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\"","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/IndexTtsCrispAsr.cs#L428-L464","documentation":"InvalidOperationException from IndexTtsCrispAsr.Speak when the POST to `/v1/audio/speech` returns a non-success HTTP status code. The server stayed up and answered, but with an error (4xx/5xx). The numeric code, the response body (via SafeReadErrorAsync), the server log, and the launch command are all captured and logged via Se.LogError before throwing.","triggerScenarios":"HTTP 400 from sending a `voice` field with path separators (the path-traversal guard) — the code deliberately omits `voice` to avoid this, so a 400 here means a payload regression; 500 from an internal backend exception during inference; 422 from unsupported input; 503 if the server is overloaded.","commonSituations":"A code change reintroduced the `voice` field (#12757 family bug); backend crashes-then-reports instead of exiting; unsupported sample rate / speed clamp violation; server version drift where the payload schema changed.","solutions":["Read the `(status): errorBody` — the server's JSON error names the exact problem.","HTTP 400 mentioning `voice`/path separators → ensure the payload omits the `voice` field (BuildSpeakPayload pattern).","HTTP 500 → inspect the server log tail for the backend stack trace and retry; if reproducible, capture and report.","Verify the CrispASR server version matches the payload schema the code expects.","Confirm the `speed` value is within 0.25–4.0 (it is clamped, but a custom build may differ)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Validate payload shape mirrors BuildSpeakPayload (no `voice` field)\nDebug.Assert(!payload.ContainsKey(\"voice\")); // path-traversal guard rejects it","typeGuard":null,"tryCatchPattern":"try { await indexEngine.Speak(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"synthesis failed (\"))\n{\n    var status = ExtractStatusCode(ex.Message);\n    if (status == 400) WarnUser(\"Payload rejected by server — check voice field / schema.\");\n    else if (status >= 500) { await Task.Delay(500, ct); await indexEngine.Speak(...); }\n    else ShowDiagnostics(ex.Message);\n}","preventionTips":["Keep the payload built via BuildSpeakPayload (no per-request `voice` field) to dodge the 400 path-traversal guard.","Pin the CrispASR server version the payload schema was authored against.","Clamp `speed` to 0.25–4.0 (already done) and avoid unsupported formats.","Unit-test the payload against the expected server schema on version bumps."],"tags":["indextts","crispasr","http-status","synthesis","tts"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}