{"record":{"id":"807167ddab01ac9b","repo":"SubtitleEdit/subtitleedit","slug":"failed-to-start-omnivoice-tts","errorCode":null,"errorMessage":"Failed to start omnivoice-tts","messagePattern":"Failed to start omnivoice-tts","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/OmniVoiceTtsCpp.cs","lineNumber":323,"sourceCode":"            psi.ArgumentList.Add(refTextPath);\n        }\n\n        // Voice-design keywords (--instruct) only shape the voice when there is no reference\n        // audio - with --ref-wav omnivoice-tts takes the voice from the clone and ignores them.\n        if (!usingReference)\n        {\n            var instruction = (Se.Settings.Video.TextToSpeech.OmniVoiceTtsCppInstruction ?? string.Empty).Trim();\n            if (!string.IsNullOrEmpty(instruction))\n            {\n                psi.ArgumentList.Add(\"--instruct\");\n                psi.ArgumentList.Add(instruction);\n            }\n        }\n\n        Se.WriteToolsLog($\"OmniVoice TTS: {exe} {string.Join(' ', psi.ArgumentList)} (voice={omniVoice}, textLen={text.Length})\");\n\n        var process = Process.Start(psi)\n            ?? throw new InvalidOperationException(\"Failed to start omnivoice-tts\");\n\n        try\n        {\n            var stderrTask = process.StandardError.ReadToEndAsync(cancellationToken);\n            var stdoutTask = process.StandardOutput.ReadToEndAsync(cancellationToken);\n\n            await process.StandardInput.WriteAsync(inputText.AsMemory(), cancellationToken);\n            process.StandardInput.Close();\n\n            await process.WaitForExitAsync(cancellationToken);\n            var stderr = await stderrTask;\n            var stdout = await stdoutTask;\n\n            if (process.ExitCode != 0 || !File.Exists(outputFileName))\n            {\n                // A process killed by the Windows loader (e.g. missing CUDA runtime, issue #13196)\n                // never reaches main, so stderr is empty and the bare exit code is all the user sees.\n                // NativeExitCodeHelper turns the known status codes into an actionable sentence.","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/OmniVoiceTtsCpp.cs#L305-L341","documentation":"InvalidOperationException thrown if Process.Start(psi) returns null when launching the omnivoice-tts CLI for a single synthesis. As with errors 243 and 250, this null path is practically unreachable with UseShellExecute=false; the realistic failure is a Win32Exception when the binary cannot execute.","triggerScenarios":"Process.Start(psi) returns null. With UseShellExecute=false, Process.Start throws rather than returning null. This guard is defensive dead code under the current configuration.","commonSituations":"Unreachable in production. If observed, check whether UseShellExecute was changed, or whether a framework-level issue caused a null return.","solutions":["Verify ProcessStartInfo.UseShellExecute is false.","If the binary genuinely won't start, the real error is a Win32Exception — wrap Process.Start in try/catch(Win32Exception) for a useful message.","Confirm the executable is not blocked (Windows Mark-of-the-Web) or quarantined.","Check execute permissions on Linux/macOS (chmod +x)."],"exampleFix":"// before\nvar process = Process.Start(psi)\n    ?? throw new InvalidOperationException(\"Failed to start omnivoice-tts\");\n\n// after — catch the real failure mode\ntry { var process = Process.Start(psi); }\ncatch (Win32Exception ex) when (ex.NativeErrorCode == 5)\n{\n    throw new InvalidOperationException(\"omnivoice-tts is not executable. Run chmod +x.\", ex);\n}\ncatch (Win32Exception ex)\n{\n    throw new InvalidOperationException($\"Failed to start omnivoice-tts: {ex.Message}\", ex);\n}","handlingStrategy":"try-catch","validationCode":"// Verify binary exists and is executable\nvar exe = GetExecutableFileName();\nif (!File.Exists(exe)) throw new FileNotFoundException(\"Missing: \" + exe, exe);","typeGuard":"null","tryCatchPattern":"catch (InvalidOperationException ex) when (ex.Message == \"Failed to start omnivoice-tts\")\n{\n    Se.LogError(ex, \"Process.Start returned null unexpectedly.\");\n    throw;\n}","preventionTips":["This path is dead code with UseShellExecute=false; handle Win32Exception instead.","Verify execute permissions (chmod +x) on Linux/macOS.","Check for Windows Mark-of-the-Web blocking on downloaded binaries."],"tags":["process","tts","omnivoice","csharp","unreachable"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}