SubtitleEdit/subtitleedit · critical · InvalidOperationException
Failed to start qwen3-tts-server
Error message
Failed to start qwen3-tts-server
What it means
Defensive throw after Process.Start(psi) returns null. On .NET the managed Process.Start only returns null when UseShellExecute is true and no process was launched; here UseShellExecute is false, so this branch is effectively unreachable in normal operation — it guards against a runtime/platform where Start unexpectedly yields null instead of throwing.
Source
Thrown at src/ui/Features/Video/TextToSpeech/Engines/Qwen3TtsCpp.cs:386
psi.ArgumentList.Add("--port");
psi.ArgumentList.Add(port.ToString());
if (OperatingSystem.IsWindows())
{
var vulkanPath = Se.Settings.Video.TextToSpeech.Qwen3TtsCppVulkanPath;
if (string.IsNullOrEmpty(vulkanPath))
{
vulkanPath = Logic.VulkanHelper.TryFindBinFolder();
}
if (!string.IsNullOrEmpty(vulkanPath) && psi.EnvironmentVariables["Path"] != null)
{
psi.EnvironmentVariables["Path"] =
psi.EnvironmentVariables["Path"]?.TrimEnd(';') + ";" + vulkanPath;
}
}
var process = Process.Start(psi)
?? throw new InvalidOperationException("Failed to start qwen3-tts-server");
Se.WriteToolsLog($"Qwen3 TTS server starting - PID: {process.Id}, "
+ $"Cmd: {exe} {string.Join(' ', psi.ArgumentList)}");
var stderrBuffer = new StringBuilder();
process.ErrorDataReceived += (_, e) =>
{
if (e.Data != null) lock (stderrBuffer) stderrBuffer.AppendLine(e.Data);
};
process.OutputDataReceived += (_, e) =>
{
if (e.Data != null) lock (stderrBuffer) stderrBuffer.AppendLine(e.Data);
};
process.BeginErrorReadLine();
process.BeginOutputReadLine();
_serverProcess = process;
_serverPort = port;View on GitHub (pinned to 17a9f07487)
Solutions
- Confirm UseShellExecute is false (it is, in the current code) — if a fork changed it, revert.
- Run the same ProcessStartInfo from a standalone harness to see whether Start returns null or throws a real Win32Exception.
- If reproducible only in a sandbox, relax the sandbox's process-creation restrictions.
- Treat as a code defect if hit on a normal desktop: file a bug, since Start should throw rather than return null here.
Defensive patterns
Strategy: validation
Validate before calling
if (psi.UseShellExecute)
throw new InvalidOperationException("UseShellExecute must be false for reliable Start.");
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 qwen3-tts-server"); }
catch (System.ComponentModel.Win32Exception ex) { Se.LogError(ex, "Process.Start failed"); throw; } Prevention
- Keep UseShellExecute=false on all server launches.
- Log the full ProcessStartInfo (FileName + ArgumentList) before Start so failures are reproducible.
- Treat a null return as a defect — investigate the runtime/sandbox, don't swallow it.
When it happens
Trigger: A future change sets UseShellExecute=true; running under a host/runtime that returns null from Start for an empty argument list; extremely rare runtime quirk where the spawn succeeds enough to return non-throwing but no Process object is produced.
Common situations: Code regression that flips UseShellExecute to true; an exotic container/jail environment where process creation is partially virtualised; effectively never seen in normal desktop use.
Related errors
- Failed to start crispasr (qwen3-tts)
- Failed to start crispasr (cosyvoice3-tts)
- Failed to start crispasr (f5-tts)
- Qwen3 TTS request failed: {ex.Message}{ServerOutput}
- Qwen3 TTS synthesis failed ({(int)response.StatusCode}): {er
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/6c4b7aeb3b13b746.
Report an issue: GitHub.