SubtitleEdit/subtitleedit · error · ArgumentException
Voice is not an F5TtsVoice
Error message
Voice is not an F5TtsVoice
What it means
ArgumentException from F5TtsCrispAsr.Speak: the passed Voice's EngineVoice is not an F5TtsVoice. F5-TTS requires its concrete voice type because it reads f5Voice.FilePath (the reference WAV) to clone from. Any other engine's voice is rejected before the file checks run.
Solutions
- Pass only voices produced by F5TtsCrispAsr.GetVoices/RefreshVoices into F5TtsCrispAsr.Speak.
- Dispatch each Voice to its owning engine by EngineVoice type.
- Add a test asserting each cast voice maps to its producing engine.
Example fix
// before
await f5TtsEngine.Speak(text, out, edgeVoice, lang, region, model, ct);
// after
var engine = voice.EngineVoice switch
{
F5TtsVoice _ => f5TtsEngine,
EdgeTtsVoice _ => edgeTtsEngine,
_ => throw new InvalidOperationException($"No engine for {voice.EngineVoice.GetType().Name}"),
};
await engine.Speak(text, out, voice, lang, region, model, ct); Defensive patterns
Strategy: type-guard
Validate before calling
if (voice.EngineVoice is not F5TtsVoice) {
Se.WriteToolsLog($"Skipping F5-TTS synthesis: voice is {voice.EngineVoice?.GetType().Name}", true);
return;
} Type guard
static bool IsF5TtsVoice(Voice voice) => voice?.EngineVoice is F5TtsVoice;
Try / catch
try { await f5TtsEngine.Speak(text, out, voice, lang, region, model, ct); }
catch (ArgumentException ex) when (ex.Message == "Voice is not an F5TtsVoice")
{ Se.WriteToolsLog($"Voice/engine mismatch for F5-TTS: {voice.EngineVoice?.GetType().Name}", true); } Prevention
- Route each Voice to the engine that produced it.
- Unit-test cast/review voices resolve to their producing engine.
- Never wrap another engine's EngineVoice in a Voice for F5-TTS.
When it happens
Trigger: Calling f5TtsEngine.Speak(...) with a Voice whose EngineVoice is not an F5TtsVoice; a dispatch bug; deserialised voice rehydrated to the wrong subtype.
Common situations: Cast row passing a global voice from another engine into F5-TTS; refactor that changed EngineVoice typing; voice JSON rehydration choosing the wrong concrete type.
Related errors
- Voice is not a CosyVoice3Voice
- Voice is not an EdgeTtsVoice
- Voice is not an ElevenLabVoice
- CrispASR executable not found. Install CrispASR via Video →…
- crispasr (f5-tts) did not report healthy within
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/b29822d60826c25c.
Report an issue: GitHub.
Appendix: source
Thrown at src/ui/Features/Video/TextToSpeech/Engines/F5TtsCrispAsr.cs:363
public Task<string[]> GetModels() => Task.FromResult(new[] { ModelKeyF16 });
public Task<TtsLanguage[]> GetLanguages(Voice voice, string? model) => Task.FromResult(Array.Empty<TtsLanguage>());
public Task<Voice[]> RefreshVoices(string language, CancellationToken cancellationToken) =>
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);View on GitHub (pinned to 17a9f07487)