SubtitleEdit/subtitleedit · error · InvalidOperationException
Failed to start crispasr (moss-tts)
Error message
Failed to start crispasr (moss-tts)
What it means
InvalidOperationException thrown if Process.Start(psi) returns null. In practice this is nearly unreachable: with UseShellExecute=false (as configured here), Process.Start either returns a non-null Process object or throws a Win32Exception. The null-check is a defensive guard for the theoretical shell-execute case or a future framework change.
Source
Thrown at src/ui/Features/Video/TextToSpeech/Engines/MossTtsCrispAsr.cs:701
if (!string.IsNullOrEmpty(languageArg))
{
psi.ArgumentList.Add("-l");
psi.ArgumentList.Add(languageArg);
}
if (UseStartupVoiceFlags)
{
psi.ArgumentList.Add("--voice");
psi.ArgumentList.Add(voicePath);
if (!string.IsNullOrEmpty(refText))
{
psi.ArgumentList.Add("--ref-text");
psi.ArgumentList.Add(refText);
}
}
var process = Process.Start(psi)
?? throw new InvalidOperationException("Failed to start crispasr (moss-tts)");
var launchCommand = FormatLaunchCommand(exe, psi.ArgumentList);
_serverLaunchCommand = launchCommand;
Se.WriteToolsLog("MOSS-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
- Check whether the ProcessStartInfo.UseShellExecute flag was inadvertently changed to true — with false, this throw path is dead code.
- If genuinely null from the framework, file a dotnet/runtime issue and temporarily retry the Process.Start call.
- Verify the executable path and arguments are valid (a bad FileName throws Win32Exception, not null, but sanity-check anyway).
- Log the psi contents to Tools log to capture the exact state at the time of failure for diagnosis.
Example fix
// before — defensive null-coalesce that is practically dead code
var process = Process.Start(psi)
?? throw new InvalidOperationException("Failed to start crispasr (moss-tts)");
// after — catch the actual failure modes (Win32Exception) instead
try { var process = Process.Start(psi); }
catch (Win32Exception ex)
{
throw new InvalidOperationException(
$"Failed to start crispasr (moss-tts): {ex.Message}. " +
"Check the executable exists and is not blocked.", ex);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the binary exists and is executable before Process.Start
var exe = GetCrispAsrExecutable();
if (!File.Exists(exe)) throw new FileNotFoundException("Missing: " + exe, exe); Type guard
null
Try / catch
catch (InvalidOperationException ex) when (ex.Message == "Failed to start crispasr (moss-tts)")
{
// Practically unreachable; the real error is Win32Exception.
// Log the psi state and fall through to a generic handler.
Se.LogError(ex, "Process.Start returned null unexpectedly.");
throw;
} Prevention
- This path is dead code with UseShellExecute=false; focus on Win32Exception instead.
- Verify the binary path and execute permissions before calling Process.Start.
- Log the full ProcessStartInfo contents on failure for diagnosis.
When it happens
Trigger: Process.Start(psi) returns null. Given UseShellExecute=false in the ProcessStartInfo, the only realistic path is a CLR/framework edge case or a future API change. Under normal conditions, if the binary cannot start, a Win32Exception (not null) is thrown instead.
Common situations: Essentially unreachable in production with the current ProcessStartInfo configuration. If it does surface, it indicates a CLR bug, a broken Process.Start shim, or code that was refactored to set UseShellExecute=true without updating this guard.
Related errors
- crispasr (moss-tts) exited during startup (code {exitCode}).
- Failed to start crispasr (omnivoice)
- MOSS-TTS (CrispASR) request failed — the connection to the c
- MOSS-TTS (CrispASR) synthesis failed ({(int)response.StatusC
- CrispASR executable not found. Install CrispASR via Video →
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/5b839c9bc9bd97a4.
Report an issue: GitHub.