{"record":{"id":"068069e6dba099f1","repo":"SubtitleEdit/subtitleedit","slug":"omnivoice-crispasr-request-failed-the-connecti","errorCode":null,"errorMessage":"OmniVoice (CrispASR) request failed — the connection to the crispasr server was dropped.","messagePattern":"OmniVoice \\(CrispASR\\) request failed — the connection to the crispasr server was dropped\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/OmniVoiceCrispAsr.cs","lineNumber":469,"sourceCode":"            response = await HttpClient.PostAsync($\"{ServerBaseUrl}/v1/audio/speech\", content, cancellationToken);\n        }\n        catch (HttpRequestException ex)\n        {\n            var serverLog = SnapshotServerLog();\n            var launchCommand = _serverLaunchCommand;\n            var died = _serverProcess?.HasExited == true;\n            if (died)\n            {\n                StopServerInternal();\n            }\n\n            var failMsg = $\"OmniVoice (CrispASR) request failed — Voice: {omniVoice}, Text: {text}, \"\n                + $\"RequestJson: {body}, ServerExited: {died}, ServerLog: {serverLog}\"\n                + LaunchCmdSuffix(launchCommand);\n            Se.LogError(ex, failMsg);\n            Se.WriteToolsLog(failMsg);\n\n            throw new InvalidOperationException(\n                (died\n                    ? \"OmniVoice (CrispASR) — the crispasr server crashed during synthesis.\"\n                    : \"OmniVoice (CrispASR) request failed — the connection to the crispasr server was dropped.\")\n                + (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 = $\"OmniVoice (CrispASR) server error {(int)response.StatusCode} {response.StatusCode} — \"\n                    + $\"Voice: {omniVoice}, Text: {text}, RequestJson: {body}, \"\n                    + $\"ResponseBody: {errorBody}, ServerLog: {serverLog}\"","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/OmniVoiceCrispAsr.cs#L451-L487","documentation":"Thrown inside the catch(HttpRequestException) handler in OmniVoiceCrispAsr.Speak when the POST to the local crispasr omnivoice server fails and _serverProcess?.HasExited is false (the 'connection dropped' branch). The server process is still alive but the HTTP connection was reset or timed out. This is the omnivoice analogue of error 240.","triggerScenarios":"HttpClient.PostAsync raises HttpRequestException while the omnivoice server process is still running. The TCP connection was dropped without the process exiting: a socket RST from the OS, the HttpClient's 30-minute timeout firing, or the server's accept loop stalling under load.","commonSituations":"Synthesizing a very long line that causes the server's HTTP handler to time out; concurrent Speak calls overwhelming the single-threaded backend; loopback connection killed by a security product; the server is alive but thrashing on swap and cannot service the request within the HttpClient timeout.","solutions":["Check the server log snapshot in the exception for any warnings about memory or queue overflow.","Retry the synthesis — the server is still alive, so a transient connection drop may resolve on the next attempt without a full restart.","Reduce input text length or split into smaller chunks.","If using a cloned voice, verify the reference WAV is short (3-10s) — a very long reference increases codec load per request.","Ensure adequate system RAM; the omnivoice backend's codec runs on CPU even on GPU systems."],"exampleFix":"// before — single attempt, any drop is fatal\nawait engine.Speak(text, ...);\n\n// after — retry once for a transient connection drop (server still alive)\nasync Task<TtsResult> SpeakWithRetry(string text, ...)\n{\n    for (int i = 0; i < 2; i++)\n    {\n        try { return await engine.Speak(text, ...); }\n        catch (InvalidOperationException ex) when (i == 0 && ex.Message.Contains(\"connection\"))\n            { await Task.Delay(2000, ct); }\n    }\n    throw;\n}","handlingStrategy":"retry","validationCode":"// Probe server health before a batch run\nif (_serverProcess is { HasExited: false })\n{\n    if (!await ProbeHealthAsync(_serverPort, TimeSpan.FromSeconds(2), ct))\n    {\n        Se.WriteToolsLog(\"Server alive but not responding to /health; will respawn.\");\n        OmniVoiceCrispAsr.StopServer();\n    }\n}","typeGuard":"null","tryCatchPattern":"catch (InvalidOperationException ex) when (ex.InnerException is HttpRequestException hre)\n{\n    // Server still alive, connection dropped — transient. Retry once.\n    if (attempt < maxRetries && _serverProcess?.HasExited == false)\n    { await Task.Delay(2000, ct); goto retry; }\n    throw;\n}","preventionTips":["Split long input text into chunks to reduce per-request load time.","Avoid concurrent Speak calls — the backend is single-threaded for inference.","Ensure adequate system RAM; the omnivoice codec runs on CPU even on GPU systems."],"tags":["network","http","tts","omnivoice","crispasr","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}