{"record":{"id":"f386b4a3b05b4afe","repo":"SubtitleEdit/subtitleedit","slug":"qwen3-tts-server-did-not-report-healthy-within-120","errorCode":null,"errorMessage":"qwen3-tts-server did not report healthy within 120s. Last output: {lastOutput}","messagePattern":"qwen3-tts-server did not report healthy within 120s\\. Last output: (.+?)","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/Qwen3TtsCpp.cs","lineNumber":436,"sourceCode":"                    _serverModelFileName = null;\n                    // An empty tail plus a 0xC00000xx code means the loader killed it before main -\n                    // NativeExitCodeHelper explains those instead of just printing the raw number.\n                    var hint = NativeExitCodeHelper.GetHint(process.ExitCode, \"Qwen3 TTS\", GetSetFolder());\n                    throw new InvalidOperationException(\n                        $\"qwen3-tts-server exited during startup (code {NativeExitCodeHelper.Describe(process.ExitCode)}).\"\n                        + (hint == null ? string.Empty : \" \" + hint)\n                        + $\" Output: {tail}\");\n                }\n                if (await ProbeHealthAsync(port, TimeSpan.FromSeconds(1), ct))\n                {\n                    return;\n                }\n                await Task.Delay(TimeSpan.FromMilliseconds(500), ct);\n            }\n\n            var lastOutput = SnapshotStderr(stderrBuffer);\n            StopServerInternal();\n            throw new TimeoutException(\n                $\"qwen3-tts-server did not report healthy within 120s. Last output: {lastOutput}\");\n        }\n        finally\n        {\n            ServerLock.Release();\n        }\n    }\n\n    private static string SnapshotStderr(StringBuilder buffer)\n    {\n        lock (buffer)\n        {\n            var s = buffer.ToString().TrimEnd();\n            return s.Length > 2000 ? s[^2000..] : s;\n        }\n    }\n\n    // Tail of the running server's captured stdout/stderr - empty when no server is tracked.","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/Qwen3TtsCpp.cs#L418-L454","documentation":"TimeoutException thrown when the qwen3-tts-server stays alive but never answers the /health probe within 120 seconds. The server is explicitly stopped (StopServerInternal) before throwing, and the last captured stderr/output is appended so the user can see where the server stalled.","triggerScenarios":"First-run model load on a slow CPU/disk exceeds 120s; the server is alive but bound to the wrong port or didn't open the expected /health route; GPU init is stuck (e.g. waiting on a driver handoff); the model is being paged in over a slow network mount.","commonSituations":"Cold start of a large model on integrated graphics; the server's /health endpoint changed name in a newer build so ProbeHealthAsync always 404s; another instance of the server already holds the port; very slow disk where mmap-loading the GGUF takes minutes.","solutions":["Read Last output — a server stuck printing init progress is just slow, not broken; a silent log suggests a port/endpoint mismatch.","Confirm no other qwen3-tts-server is already bound to the loopback port (the code picks a free port, but a stale zombie can race).","Warm the model once outside the app (run the server manually) to confirm it eventually becomes healthy, then increase the deadline if the platform legitimately needs longer.","Update ProbeHealthAsync's route if the server build renamed /health."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm the health route exists for this server build before relying on the 120s wait.\nvar routeOk = await ProbeHealthAsync(port, TimeSpan.FromSeconds(1), ct);","typeGuard":null,"tryCatchPattern":"try { await EnsureServerRunningAsync(modelFileName, ct); }\ncatch (TimeoutException ex)\n{\n    // Server alive but slow — surface Last output; offer to retry with a warmed model.\n    Se.LogError(ex);\n    throw;\n}","preventionTips":["Warm the model once outside the app to confirm it eventually becomes healthy.","Keep the health-probe route in sync with the server build.","Don't leave zombie servers holding the loopback port."],"tags":["tts","qwen3-tts","server-process","timeout","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}