SubtitleEdit/subtitleedit · error · InvalidOperationException

F5-TTS (CrispASR) requires a reference voice WAV. Import one

Error message

F5-TTS (CrispASR) requires a reference voice WAV. Import one via the voice settings, then pick it in the voice combo. Reference WAV should be 24 kHz mono (3-10 s of clean speech) with an adjacent .txt file holding the spoken transcription.

What it means

InvalidOperationException from F5TtsCrispAsr.Speak: the F5TtsVoice was accepted (type matched) but its FilePath is null/empty. F5-TTS is a voice-cloning engine — it always needs a reference WAV (24 kHz mono, 3–10 s clean speech) plus an adjacent .txt transcription sidecar. Unlike CosyVoice3, this is a hard throw rather than an error result.

Source

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

        GetVoices(language);

    public async Task<TtsResult> Speak(
        string text,
        string outputFolder,
        Voice voice,
        TtsLanguage? language,
        string? region,
        string? model,
        CancellationToken cancellationToken)
    {
        if (voice.EngineVoice is not F5TtsVoice f5Voice)
        {
            throw new ArgumentException("Voice is not an F5TtsVoice");
        }

        if (string.IsNullOrEmpty(f5Voice.FilePath))
        {
            throw new InvalidOperationException(
                "F5-TTS (CrispASR) requires a reference voice WAV. "
                + "Import one via the voice settings, then pick it in the voice combo. "
                + "Reference WAV should be 24 kHz mono (3-10 s of clean speech) with an "
                + "adjacent .txt file holding the spoken transcription.");
        }

        var refText = TryReadRefText(f5Voice.FilePath);
        var modelKey = ResolveModelKey(model);
        await EnsureServerRunningAsync(modelKey, f5Voice.FilePath, refText, cancellationToken);

        var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetFolder), Guid.NewGuid() + ".wav");

        var speed = Math.Clamp(Se.Settings.Video.TextToSpeech.F5TtsCrispAsrSpeed, 0.25, 4.0);
        // Deliberately NO `voice` / `ref_text` field: the server rejects absolute paths outright
        // (HTTP 400, "'voice' must not contain … path separators" — path-traversal guard), so
        // sending f5Voice.FilePath failed every synthesis. The f5-tts backend reads the reference
        // from the startup --voice / --ref-text flags (no bare-name resolution), and the server
        // restarts on (voice, ref-text) change — see EnsureServerRunningAsync. Same bug family as

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Import a 24 kHz mono reference WAV (3–10 s clean speech) via the F5-TTS voice settings, then select it in the voice combo.
  2. Place an adjacent .txt file with the spoken transcription next to the WAV so ref-text is auto-read.
  3. Validate FilePath is set before queueing the segment so the run aborts earlier with a clearer message.

Example fix

// before
var f5Voice = new F5TtsVoice { Name = "my clone" }; // no FilePath
await f5TtsEngine.Speak(text, out, new Voice(f5Voice), lang, region, model, ct);

// after
var f5Voice = new F5TtsVoice { Name = "my clone", FilePath = @"C:\voices\ref.wav" };
await f5TtsEngine.Speak(text, out, new Voice(f5Voice), lang, region, model, ct);
Defensive patterns

Strategy: validation

Validate before calling

if (voice.EngineVoice is F5TtsVoice f5 && string.IsNullOrEmpty(f5.FilePath)) {
    Se.WriteToolsLog("F5-TTS voice has no reference WAV; prompt the user to import one.", true);
    return new TtsResult { Text = text, FileName = string.Empty, Error = true, ErrorMessage = "Import a 24 kHz mono reference WAV first." };
}

Try / catch

try { await f5TtsEngine.Speak(text, out, voice, lang, region, model, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("requires a reference voice WAV", StringComparison.Ordinal))
{ Se.WriteToolsLog("Import a 24 kHz mono reference WAV for this F5-TTS voice, then retry.", true); }

Prevention

When it happens

Trigger: Speak proceeds past the type check, then string.IsNullOrEmpty(f5Voice.FilePath) is true; the voice was created without importing a reference WAV, or the FilePath was cleared.

Common situations: An F5TtsVoice constructed without an import; the imported WAV file path was reset/lost on settings reload; a cast row selected a default F5 voice that has no reference attached.

Related errors


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