{"record":{"id":"7cead05f5cbbed84","repo":"SubtitleEdit/subtitleedit","slug":"crispasr-moss-tts-did-not-report-healthy-within","errorCode":null,"errorMessage":"crispasr (moss-tts) did not report healthy within {10|60} minutes. Last output: {lastOutput}{LaunchCmdSuffix}","messagePattern":"crispasr \\(moss-tts\\) did not report healthy within (.+?) minutes\\. Last output: (.+?)(.+?)","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/MossTtsCrispAsr.cs","lineNumber":761,"sourceCode":"                    _serverModelKey = null;\n                    _serverVoicePath = null;\n                    _serverRefText = null;\n                    _serverLanguageArg = null;\n                    throw new InvalidOperationException(\n                        $\"crispasr (moss-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 (moss-tts) did not report healthy within {(hasLocalModel ? 10 : 60)} 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":743,"sourceCodeEnd":779,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/MossTtsCrispAsr.cs#L743-L779","documentation":"TimeoutException thrown when the crispasr moss-tts process stays alive but never responds to GET /health within the deadline (10 minutes if the model is already staged locally, 60 minutes if first-run auto-download is in progress). The server is stopped via StopServerInternal before throwing.","triggerScenarios":"The EnsureServerRunningAsync while-loop runs until DateTime.UtcNow exceeds the deadline without ProbeHealthAsync ever returning true and without the process exiting. The server is presumably loading a large model or downloading it, but 10/60 minutes elapsed without the /health endpoint going green.","commonSituations":"First-run download of the ~20.5 GB F16 model on a slow or throttled connection; the model is on a slow network drive and disk I/O is the bottleneck; CPU-only inference on an underpowered machine where model load takes exceptionally long; the server is stuck in a retry loop downloading a model (Hugging Face rate limiting); the /health endpoint exists but returns non-200 (ProbeHealthAsync only returns true on IsSuccessStatusCode).","solutions":["Check the 'Last output' in the exception — if it shows download progress, the connection is too slow; consider pre-downloading the GGUF manually and placing it in Se.CrispAsrFolder/models.","If the output shows the model loaded but no health line, verify the crispasr version supports the /health endpoint (older builds may not).","Switch to the Q4_K quant (~10.5 GB) to halve load time on first run.","On a machine with enough RAM, ensure the model is on a local SSD, not a network/spinning drive.","If downloading, check for Hugging Face rate limits or use the --auto-download path with a stable connection."],"exampleFix":"// before — fixed 10/60 min deadline\nvar deadline = DateTime.UtcNow.AddMinutes(hasLocalModel ? 10 : 60);\nwhile (DateTime.UtcNow < deadline)\n{\n    if (await ProbeHealthAsync(port, TimeSpan.FromSeconds(2), ct)) return;\n    await Task.Delay(TimeSpan.FromSeconds(1), ct);\n}\nthrow new TimeoutException(...);\n\n// after — make the deadline configurable for slow machines\nvar minutes = hasLocalModel\n    ? Se.Settings.Video.TextToSpeech.MossTtsStartupTimeoutMinutes\n    : 60;\nvar deadline = DateTime.UtcNow.AddMinutes(minutes);","handlingStrategy":"retry","validationCode":"// Check if models are already staged to predict load time\nvar hasLocalModel = MossTtsCrispAsr.AreModelsInstalled(modelKey);\nvar expectedMinutes = hasLocalModel ? 10 : 60;\nif (estimatedLoadTimeMinutes > expectedMinutes)\n    Se.WriteToolsLog($\"Warning: model load may exceed the {expectedMinutes} min deadline on this hardware.\");","typeGuard":"null","tryCatchPattern":"catch (TimeoutException ex) when (ex.Message.Contains(\"did not report healthy\"))\n{\n    // The server may still be loading. Retry once with a longer wait,\n    // or pre-download the model and retry with hasLocalModel=true (faster deadline path).\n    if (attempt == 0) { await Task.Delay(TimeSpan.FromMinutes(2), ct); goto retry; }\n    throw;\n}","preventionTips":["Pre-download models via the TTS download dialog so first-run uses the 10-min deadline, not 60.","Place models on a local SSD, not a network drive.","Monitor disk and network throughput during model load/download."],"tags":["timeout","startup","tts","moss-tts","crispasr","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}