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
- 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.
- Retry the synthesis on a shorter text segment; split long subtitle lines and generate multiple clips.
- 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.
- 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.
- 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
- Monitor server health via ProbeHealthAsync before batch synthesis runs.
- Split long subtitle lines into <500-character chunks to avoid server-side timeouts.
- Ensure the machine has sufficient RAM/VRAM for the selected quant before starting a batch.
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
- MOSS-TTS (CrispASR) synthesis failed ({(int)response.StatusC
- OmniVoice (CrispASR) request failed — the connection to the
- MOSS-TTS (CrispASR) — the crispasr server crashed during syn
- CrispASR executable not found. Install CrispASR via Video →
- Failed to start crispasr (moss-tts)
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/3125e3dcee0f8d14.
Report an issue: GitHub.