SubtitleEdit/subtitleedit · critical · InvalidOperationException
Failed to start crispasr (qwen3-tts)
Error message
Failed to start crispasr (qwen3-tts)
What it means
Defensive InvalidOperationException thrown when Process.Start(psi) returns null while launching crispasr. As with the qwen3-tts equivalent, UseShellExecute is false here so Start should throw rather than return null in normal operation; this guard catches a runtime/platform quirk that yields null instead.
Source
Thrown at src/ui/Features/Video/TextToSpeech/Engines/Qwen3TtsCrispAsr.cs:986
psi.ArgumentList.Add(codec);
}
if (!hasLocalTalker || !hasLocalCodec)
{
psi.ArgumentList.Add("--auto-download");
}
psi.ArgumentList.Add("--host");
psi.ArgumentList.Add("127.0.0.1");
psi.ArgumentList.Add("--port");
psi.ArgumentList.Add(port.ToString());
// The crispasr server gates /v1/audio/speech requests with a `voice` field behind
// --voice-dir being set. Pointing at our voices folder satisfies the gate and also
// makes /v1/voices reflect any imported reference WAVs.
psi.ArgumentList.Add("--voice-dir");
psi.ArgumentList.Add(GetSetVoicesFolder());
CrispAsrTtsProvenance.AddServerMarkingArgs(psi.ArgumentList, exe);
var process = Process.Start(psi)
?? throw new InvalidOperationException("Failed to start crispasr (qwen3-tts)");
var launchCommand = FormatLaunchCommand(exe, psi.ArgumentList);
_serverLaunchCommand = launchCommand;
Se.WriteToolsLog("Qwen3 TTS (CrispASR) server starting — "
+ $"PID: {process.Id}, "
+ $"Cmd: {launchCommand}");
lock (_serverLog) _serverLog.Clear();
process.ErrorDataReceived += (_, e) =>
{
if (e.Data != null) lock (_serverLog) _serverLog.AppendLine(e.Data);
};
process.OutputDataReceived += (_, e) =>
{
if (e.Data != null) lock (_serverLog) _serverLog.AppendLine(e.Data);
};
process.BeginErrorReadLine();
process.BeginOutputReadLine();View on GitHub (pinned to 17a9f07487)
Solutions
- Confirm UseShellExecute stays false in the launch path.
- Reproduce the ProcessStartInfo in a standalone harness to see whether Start returns null or throws a real exception.
- If isolated to a sandbox, relax its process-creation policy.
- Treat as a defect on normal desktops — file a bug; Start is expected to throw, not return null.
Defensive patterns
Strategy: validation
Validate before calling
if (psi.UseShellExecute)
throw new InvalidOperationException("UseShellExecute must be false.");
if (string.IsNullOrWhiteSpace(psi.FileName))
throw new InvalidOperationException("Executable path is empty."); Try / catch
try { var process = Process.Start(psi) ?? throw new InvalidOperationException("Failed to start crispasr (qwen3-tts)"); }
catch (System.ComponentModel.Win32Exception ex) { Se.LogError(ex); throw; } Prevention
- Keep UseShellExecute=false.
- Log the full launch command before Start.
- Treat a null return as a defect — investigate the runtime/sandbox.
When it happens
Trigger: A future change sets UseShellExecute=true; a host runtime that returns null for an empty/invalid argument list instead of throwing; an exotic sandboxed environment that partially virtualises process creation.
Common situations: Code regression flipping UseShellExecute; unusual container/jail setups; effectively unreachable on normal desktop runtimes.
Related errors
- Failed to start crispasr (cosyvoice3-tts)
- Failed to start crispasr (f5-tts)
- Failed to start qwen3-tts-server
- Failed to start crispasr (indextts)
- MOSS-TTS (CrispASR) request failed — the connection to the c
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/637a165c66fc7f8a.
Report an issue: GitHub.