SubtitleEdit/subtitleedit · error · InvalidOperationException

OmniVoice (CrispASR) request failed — the connection to the

Error message

OmniVoice (CrispASR) request failed — the connection to the crispasr server was dropped.

What it means

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.

Source

Thrown at src/ui/Features/Video/TextToSpeech/Engines/OmniVoiceCrispAsr.cs:469

            response = await HttpClient.PostAsync($"{ServerBaseUrl}/v1/audio/speech", content, cancellationToken);
        }
        catch (HttpRequestException ex)
        {
            var serverLog = SnapshotServerLog();
            var launchCommand = _serverLaunchCommand;
            var died = _serverProcess?.HasExited == true;
            if (died)
            {
                StopServerInternal();
            }

            var failMsg = $"OmniVoice (CrispASR) request failed — Voice: {omniVoice}, Text: {text}, "
                + $"RequestJson: {body}, ServerExited: {died}, ServerLog: {serverLog}"
                + LaunchCmdSuffix(launchCommand);
            Se.LogError(ex, failMsg);
            Se.WriteToolsLog(failMsg);

            throw new InvalidOperationException(
                (died
                    ? "OmniVoice (CrispASR) — the crispasr server crashed during synthesis."
                    : "OmniVoice (CrispASR) request failed — the connection to the crispasr server was dropped.")
                + (string.IsNullOrEmpty(serverLog) ? string.Empty : $"{Environment.NewLine}Server log:{Environment.NewLine}{serverLog}")
                + LaunchCmdSuffix(launchCommand),
                ex);
        }

        using (response)
        {
            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await SafeReadErrorAsync(response, cancellationToken);
                var serverLog = SnapshotServerLog();
                var launchCommand = _serverLaunchCommand;
                var errMsg = $"OmniVoice (CrispASR) server error {(int)response.StatusCode} {response.StatusCode} — "
                    + $"Voice: {omniVoice}, Text: {text}, RequestJson: {body}, "
                    + $"ResponseBody: {errorBody}, ServerLog: {serverLog}"

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Check the server log snapshot in the exception for any warnings about memory or queue overflow.
  2. Retry the synthesis — the server is still alive, so a transient connection drop may resolve on the next attempt without a full restart.
  3. Reduce input text length or split into smaller chunks.
  4. If using a cloned voice, verify the reference WAV is short (3-10s) — a very long reference increases codec load per request.
  5. Ensure adequate system RAM; the omnivoice backend's codec runs on CPU even on GPU systems.

Example fix

// before — single attempt, any drop is fatal
await engine.Speak(text, ...);

// after — retry once for a transient connection drop (server still alive)
async Task<TtsResult> SpeakWithRetry(string text, ...)
{
    for (int i = 0; i < 2; i++)
    {
        try { return await engine.Speak(text, ...); }
        catch (InvalidOperationException ex) when (i == 0 && ex.Message.Contains("connection"))
            { await Task.Delay(2000, ct); }
    }
    throw;
}
Defensive patterns

Strategy: retry

Validate before calling

// Probe server health before a batch run
if (_serverProcess is { HasExited: false })
{
    if (!await ProbeHealthAsync(_serverPort, TimeSpan.FromSeconds(2), ct))
    {
        Se.WriteToolsLog("Server alive but not responding to /health; will respawn.");
        OmniVoiceCrispAsr.StopServer();
    }
}

Type guard

null

Try / catch

catch (InvalidOperationException ex) when (ex.InnerException is HttpRequestException hre)
{
    // Server still alive, connection dropped — transient. Retry once.
    if (attempt < maxRetries && _serverProcess?.HasExited == false)
    { await Task.Delay(2000, ct); goto retry; }
    throw;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/068069e6dba099f1. Report an issue: GitHub.