{"record":{"id":"c5008eddfd9fa485","repo":"SubtitleEdit/subtitleedit","slug":"crispasr-qwen3-tts-did-not-report-healthy-within","errorCode":null,"errorMessage":"crispasr (qwen3-tts) did not report healthy within {hasLocalTalker && hasLocalCodec ? 5 : 30} minutes. Last output: {lastOutput}{LaunchCmdSuffix}","messagePattern":"crispasr \\(qwen3-tts\\) did not report healthy within (.+?) minutes\\. Last output: (.+?)(.+?)","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/Qwen3TtsCrispAsr.cs","lineNumber":1039,"sourceCode":"                    _serverProcess = null;\n                    _serverPort = 0;\n                    _serverModelKey = null;\n                    _serverLaunchCommand = null;\n                    throw new InvalidOperationException(\n                        $\"crispasr (qwen3-tts) exited during startup (code {exitCode}). Output: {tail}\"\n                        + LaunchCmdSuffix(exitedLaunchCommand));\n                }\n                if (await ProbeHealthAsync(port, TimeSpan.FromSeconds(2), ct))\n                {\n                    return;\n                }\n                await Task.Delay(TimeSpan.FromSeconds(1), ct);\n            }\n\n            var lastOutput = SnapshotServerLog();\n            var timeoutLaunchCommand = _serverLaunchCommand;\n            StopServerInternal();\n            throw new TimeoutException(\n                $\"crispasr (qwen3-tts) did not report healthy within {(hasLocalTalker && hasLocalCodec ? 5 : 30)} minutes. Last output: {lastOutput}\"\n                + LaunchCmdSuffix(timeoutLaunchCommand));\n        }\n        finally\n        {\n            ServerLock.Release();\n        }\n    }\n\n    private static string SnapshotServerLog()\n    {\n        lock (_serverLog)\n        {\n            var s = _serverLog.ToString().TrimEnd();\n            return s.Length > 2000 ? s[^2000..] : s;\n        }\n    }\n","sourceCodeStart":1021,"sourceCodeEnd":1057,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/Qwen3TtsCrispAsr.cs#L1021-L1057","documentation":"TimeoutException thrown when crispasr stays alive but never answers /health within the deadline. The deadline is conditional: 5 minutes when both talker and codec GGUFs are already staged locally, 30 minutes when --auto-download must fetch them (~2 GB talker + ~986 MB codec) on first run. The server is stopped and the last log plus launch command are appended.","triggerScenarios":"First-run download over a slow/unreliable link exceeds 30 min; the server is alive but /health was renamed so the probe always fails; GPU init hangs; the model is on a slow network mount and mmap-loads for minutes; the auto-download is blocked by a proxy and the server waits indefinitely.","commonSituations":"Offline or metered-network first run; a newer crispasr build that changed the health route; a zombie server holding the port; very large model on integrated graphics.","solutions":["Pre-stage the talker and codec GGUFs locally so the 5-minute (not 30-minute) deadline applies and no network download is needed.","Check Last output — a server printing download progress is just slow; a silent log suggests a route/port mismatch.","Confirm the probe route in ProbeHealthAsync matches this crispasr build's health endpoint.","If on a slow link, run the first synthesis once outside the app (or pre-download the cache) so subsequent starts hit the short deadline."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-stage models locally so the short (5 min) deadline applies.\nvar hasLocalTalker = IsValidLocalModelFile(GetTalkerPath(modelKey), GetTalkerFileName(modelKey));\nvar hasLocalCodec = IsValidLocalModelFile(GetCodecPath(), CodecFileName);\nif (!hasLocalTalker || !hasLocalCodec)\n    await PreDownloadCrispAsrModelsAsync(modelKey);","typeGuard":null,"tryCatchPattern":"try { await EnsureServerRunningAsync(modelKey, ct); }\ncatch (TimeoutException ex)\n{ Se.LogError(ex); /* offer retry after pre-staging models */ throw; }","preventionTips":["Pre-stage the talker and codec GGUFs to avoid the 30-minute first-run window.","Keep the health-probe route in sync with the crispasr build.","Confirm network/proxy allows the auto-download if models aren't local."],"tags":["tts","crispasr","server-process","timeout","download","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}