SubtitleEdit/subtitleedit · error · TimeoutException

crispasr (f5-tts) did not report healthy within {(hasLocalTa

Error message

crispasr (f5-tts) did not report healthy within {(hasLocalTalker ? 5 : 20)} minutes. Last output: {lastOutput}{LaunchCmdSuffix}

What it means

Thrown as a TimeoutException when the f5-tts crispasr server starts and stays alive but never answers `/health` within the deadline — 5 minutes if a local talker GGUF is present, 20 minutes otherwise (to allow the ~953 MB first-run auto-download). The server process is stopped and the last 2000 chars of its log are attached.

Source

Thrown at src/ui/Features/Video/TextToSpeech/Engines/F5TtsCrispAsr.cs:651

                    _serverLaunchCommand = null;
                    _serverModelKey = null;
                    _serverVoicePath = null;
                    _serverRefText = null;
                    throw new InvalidOperationException(
                        $"crispasr (f5-tts) exited during startup (code {exitCode}). Output: {tail}"
                        + LaunchCmdSuffix(exitedLaunchCommand));
                }
                if (await ProbeHealthAsync(port, TimeSpan.FromSeconds(2), ct))
                {
                    return;
                }
                await Task.Delay(TimeSpan.FromSeconds(1), ct);
            }

            var lastOutput = SnapshotServerLog();
            var timeoutLaunchCommand = _serverLaunchCommand;
            StopServerInternal();
            throw new TimeoutException(
                $"crispasr (f5-tts) did not report healthy within {(hasLocalTalker ? 5 : 20)} minutes. Last output: {lastOutput}"
                + LaunchCmdSuffix(timeoutLaunchCommand));
        }
        finally
        {
            ServerLock.Release();
        }
    }

    private static string SnapshotServerLog()
    {
        lock (_serverLog)
        {
            var s = _serverLog.ToString().TrimEnd();
            return s.Length > 2000 ? s[^2000..] : s;
        }
    }

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Check the `Last output:` tail — a still-downloading or still-loading server prints progress lines.
  2. If on first run, pre-stage the talker GGUF locally (place it at GetTalkerPath path) so the 5-minute deadline applies instead of 20.
  3. Ensure no firewall/VPN blocks loopback traffic to `127.0.0.1`.
  4. Close other GPU-heavy processes so the server can finish loading within the window.
  5. If the server is genuinely slow to init on your hardware, increase the deadline constant only after confirming the server eventually goes healthy when run manually.
Defensive patterns

Strategy: retry

Validate before calling

// Before Speak, sanity-check the model is present so the short 5-min deadline applies
if (!engine.HasLocalTalker(modelKey))
{
    WarnUser("First run downloads ~953 MB and may need the full 20-min window on a slow link.");
}

Try / catch

try { await f5Engine.Speak(...); }
catch (TimeoutException ex) when (ex.Message.Contains("did not report healthy"))
{
    // Often a slow first-run download. Retry once after the user confirms the network/disk is free.
    if (await ConfirmRetry("Server did not become healthy in time. Retry?"))
        await f5Engine.Speak(...);
}

Prevention

When it happens

Trigger: The model download is slow/stalled on a poor network so the server never finishes loading; the server binds but `/health` hangs (deadlock in the backend); a host firewall blocks the loopback `127.0.0.1:port` health GET; the process is alive but pegged at 100% CPU initializing while the probe times out.

Common situations: Slow or proxy-filtered corporate network blocking the model CDN; a CrispASR version whose `/health` route changed; concurrent runs saturating the GPU so init never completes; large model on an underpowered machine.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/3d03ac92e09d0e63. Report an issue: GitHub.