SubtitleEdit/subtitleedit · error · InvalidOperationException

MOSS-TTS (CrispASR) request failed — the connection to the c

Error message

MOSS-TTS (CrispASR) request failed — the connection to the crispasr server was dropped.

What it means

Thrown inside a catch(HttpRequestException) handler in MossTtsCrispAsr.Speak after HttpClient.PostAsync to the local crispasr server's /v1/audio/speech endpoint fails. The code checks _serverProcess?.HasExited and, because it is false (the else branch of the ternary), concludes the server process is still alive but the TCP connection was dropped mid-request. The original HttpRequestException is wrapped as the InnerException of the InvalidOperationException.

Source

Thrown at src/ui/Features/Video/TextToSpeech/Engines/MossTtsCrispAsr.cs:476

            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 = $"MOSS-TTS (CrispASR) request failed — Voice: {mossVoice}, Text: {text}, "
                + $"RequestJson: {body}, ServerExited: {died}, ServerLog: {serverLog}"
                + LaunchCmdSuffix(launchCommand);
            Se.LogError(ex, failMsg);
            Se.WriteToolsLog(failMsg);

            throw new InvalidOperationException(
                (died
                    ? "MOSS-TTS (CrispASR) — the crispasr server crashed during synthesis."
                    : "MOSS-TTS (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 = $"MOSS-TTS (CrispASR) server error {(int)response.StatusCode} {response.StatusCode} — "
                    + $"Voice: {mossVoice}, Text: {text}, RequestJson: {body}, "
                    + $"ResponseBody: {errorBody}, ServerLog: {serverLog}"

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Check the Tools log / error message for the 'Server log' tail — the server's own stderr often shows the OOM or CUDA error that caused it to stop accepting requests.
  2. Retry the synthesis on a shorter text segment; split long subtitle lines and generate multiple clips.
  3. Ensure the machine has enough free RAM/VRAM for the selected MOSS-TTS quant (Q4_K needs ~10.5 GB; F16 needs ~20.5 GB) — switch to a smaller quant in the model dropdown.
  4. If it recurs, call StopServer() (or restart SE) to force a clean server respawn, since a zombie server process that stopped listening is still alive and blocks EnsureServerRunningAsync's MatchesCurrent check.
  5. Add an exception for the SE folder / crispasr executable in antivirus real-time protection.

Example fix

// before: a single long line that may exhaust the server
await engine.Speak(veryLongText, ...);

// after: split and let the server recover between calls
var chunks = SplitLongText(veryLongText, maxChars: 500);
foreach (var chunk in chunks)
{
    try { await engine.Speak(chunk, ...); }
    catch (InvalidOperationException) when (serverMayHaveStalled)
    {
        MossTtsCrispAsr.StopServer(); // force respawn on next call
        throw;
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// Before calling Speak, verify the server is still listening
if (_serverProcess is { HasExited: false })
{
    var healthy = await ProbeHealthAsync(_serverPort, TimeSpan.FromSeconds(2), ct);
    if (!healthy) { MossTtsCrispAsr.StopServer(); /* force respawn */ }
}

Type guard

null

Try / catch

catch (InvalidOperationException ex) when (ex.InnerException is HttpRequestException)
{
    // The server process is alive but the connection dropped.
    // Retry once after a short delay; if it fails again, StopServer to force a clean respawn.
    if (attempt < maxRetries) { await Task.Delay(2000, ct); goto retry; }
    MossTtsCrispAsr.StopServer();
    throw;
}

Prevention

When it happens

Trigger: POST to http://127.0.0.1:{port}/v1/audio/speech raises HttpRequestException while _serverProcess.HasExited is false. This happens when: the OS resets the TCP socket (RST) during a long synthesis; the server's internal HTTP handler dies but the process lingers; the HttpClient 30-minute timeout elapses; or antivirus/firewall interferes with loopback traffic.

Common situations: Synthesizing a very long subtitle line that exceeds the server's per-request memory or time budget; the server is CPU-thrashing on a low-RAM machine and the accept loop stalls; a second concurrent Speak call hit the server while it was already saturating all threads; Windows Defender or a corporate firewall briefly inspects and kills the loopback connection.

Related errors


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