{"record":{"id":"e6745044f31a8bc4","repo":"SubtitleEdit/subtitleedit","slug":"process-startinfo-filename-process-startinfo-a","errorCode":null,"errorMessage":"\"{process.StartInfo.FileName} {process.StartInfo.Arguments}\" did not finish within {timeout.TotalSeconds:0} seconds and was killed.","messagePattern":"\"(.+?) (.+?)\" did not finish within (.+?) seconds and was killed\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/ProcessExtensions.cs","lineNumber":61,"sourceCode":"    public static async Task StartAndWaitAsync(this Process process, CancellationToken cancellationToken, TimeSpan timeout)\n    {\n        process.StartProcess();\n\n        using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n        timeoutCts.CancelAfter(timeout);\n        try\n        {\n            await process.WaitForExitAsync(timeoutCts.Token);\n        }\n        catch (OperationCanceledException)\n        {\n            KillNoError(process);\n            if (cancellationToken.IsCancellationRequested)\n            {\n                throw; // user cancel - propagate as cancellation, not as a timeout\n            }\n\n            throw new TimeoutException(\n                $\"\\\"{process.StartInfo.FileName} {process.StartInfo.Arguments}\\\" did not finish within {timeout.TotalSeconds:0} seconds and was killed.\");\n        }\n    }\n\n    private static void KillNoError(Process process)\n    {\n        try\n        {\n            process.Kill(entireProcessTree: true);\n        }\n        catch\n        {\n            // best-effort - the process may have exited in the meantime\n        }\n    }\n}\n","sourceCodeStart":43,"sourceCodeEnd":78,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/ProcessExtensions.cs#L43-L78","documentation":"Thrown by the three-argument ProcessExtensions.StartAndWaitAsync(process, cancellationToken, timeout) when a started child process (typically ffmpeg or a TTS engine CLI) does not exit before the supplied TimeSpan deadline. The method builds a linked CancellationTokenSource that cancels after the timeout; when WaitForExitAsync is cancelled it kills the entire process tree and then distinguishes a user-initiated cancellation (re-thrown as OperationCanceledException) from a genuine overrun (re-thrown as this TimeoutException naming the exact command line). The goal (issue #12093) is to surface a wedged child — e.g. ffmpeg blocked on a stdin prompt — as an error instead of freezing the generation/merge pipeline forever.","triggerScenarios":"Calling StartAndWaitAsync with a timeout where the child runs past the deadline: ffmpeg invoked without -nostdin so it blocks reading stdin; a TTS engine CLI waiting on an interactive prompt; an extremely long video with a too-short timeout; antivirus holding the child process; a network-dependent generation stalling.","commonSituations":"ffmpeg launched without -nostdin/-y on Windows where a console is attached; corrupted input media causing ffmpeg to loop; a slow remote TTS endpoint exceeding the configured window; an older/region-locked engine binary that hangs; timeout value set too low for HD/4K content.","solutions":["Inspect the tools log (Se.WriteToolsLog) for the exact command line that hung and reproduce it manually to see the prompt/blocker.","Ensure ffmpeg/child arguments include -nostdin and -y so it never waits on the terminal.","Increase the TimeSpan passed to StartAndWaitAsync for long-running operations (merge/generation of long videos).","Verify the engine binary version matches expectations and the input file is not corrupt (ffprobe the source).","If antivirus is interfering, add an exclusion for the SE temp/tools folder."],"exampleFix":"// before\nawait process.StartAndWaitAsync(process, ct, TimeSpan.FromSeconds(30));\n\n// after - non-interactive args and a window sized to the work\npsi.Arguments = \"-nostdin -y \" + psi.Arguments;\nawait process.StartAndWaitAsync(process, ct, TimeSpan.FromSeconds(300));","handlingStrategy":"try-catch","validationCode":"// Ensure the child is non-interactive and the timeout fits the work before starting.\nif (!process.StartInfo.Arguments.Contains(\"-nostdin\"))\n    process.StartInfo.Arguments = \"-nostdin -y \" + process.StartInfo.Arguments;\nvar estimatedSeconds = Math.Max(60, sourceDurationSeconds * 2);\nvar timeout = TimeSpan.FromSeconds(estimatedSeconds);","typeGuard":null,"tryCatchPattern":"try\n{\n    await process.StartAndWaitAsync(process, ct, timeout);\n}\ncatch (TimeoutException ex)\n{\n    SeLogger.Error(ex, \"Timed out: \" + ex.Message); // message already names the command\n    throw; // or surface to user and offer retry with a longer window\n}\ncatch (OperationCanceledException) when (ct.IsCancellationRequested)\n{\n    // user cancel - not a timeout\n}","preventionTips":["Always pass -nostdin/-y to ffmpeg-style children so they never block on the terminal.","Size the timeout to the workload (longer for HD/4K merge and remote TTS).","Log the command line before StartAndWaitAsync so a timeout is reproducible."],"tags":["process","timeout","ffmpeg","tts","cancellation","native"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}