SubtitleEdit/subtitleedit · error · ArgumentException
Voice is not a AllTalkVoice
Error message
Voice is not a AllTalkVoice
What it means
AllTalk.Speak pattern-matches voice.EngineVoice to AllTalkVoice; any other engine's voice type triggers ArgumentException before any network call. This is a programmer/config error, not a transient failure — the wrong voice object reached the AllTalk engine.
Source
Thrown at src/ui/Features/Video/TextToSpeech/Engines/AllTalk.cs:161
{
var ms = new MemoryStream();
await _ttsDownloadService.DownloadAllTalkVoiceList(ms, null, cancellationToken);
await File.WriteAllBytesAsync(Path.Combine(GetSetAllTalkFolder(), JsonFileName), ms.ToArray(), cancellationToken);
return await 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 AllTalkVoice allTalkVoice)
{
throw new ArgumentException("Voice is not a AllTalkVoice");
}
var languageCode = language != null ? language.Code : "en";
Se.WriteToolsLog($"AllTalk: voice={allTalkVoice.Voice}, language={languageCode}, textLen={text.Length}");
var allTalkFileNameOutputFileName = await _ttsDownloadService.AllTalkVoiceSpeak(text, allTalkVoice, languageCode, cancellationToken);
// The server reports its output as a local path. It can be empty (unexpected response
// JSON) or unreachable from here (server on another machine / different working dir) -
// File.Move then threw unhandled instead of failing the segment like other engines.
if (string.IsNullOrWhiteSpace(allTalkFileNameOutputFileName) || !File.Exists(allTalkFileNameOutputFileName))
{
var error = $"AllTalk did not produce a readable output file (reported path: \"{allTalkFileNameOutputFileName}\") - is the server running on this machine?";
Se.WriteToolsLog("AllTalk: " + error, true);
return new TtsResult { Text = text, FileName = string.Empty, Error = true, ErrorMessage = error };
}
var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetAllTalkFolder), Guid.NewGuid() + ".wav");
File.Move(allTalkFileNameOutputFileName, outputFileName);View on GitHub (pinned to 17a9f07487)
Solutions
- Re-select the voice from the AllTalk voice list after switching to the AllTalk engine.
- Clear persisted voice mappings that reference other engines before invoking Speak.
- Filter the voice picker UI to voices owned by the active engine only.
Example fix
// before: pass any voice
await _allTalk.Speak(text, out, voice, lang, region, model, ct);
// after: guard at the call site
if (voice.EngineVoice is not AllTalkVoice)
throw new InvalidOperationException($"Voice {voice.Name} is not an AllTalk voice; re-select it.");
await _allTalk.Speak(text, out, voice, lang, region, model, ct); Defensive patterns
Strategy: type-guard
Validate before calling
if (voice.EngineVoice is not AllTalkVoice) return Invalid($"Voice {voice.Name} is not an AllTalk voice"); Type guard
static bool IsAllTalkVoice(Voice v) => v.EngineVoice is AllTalkVoice;
Try / catch
try { await allTalk.Speak(...); }
catch (ArgumentException ex) when (ex.Message.Contains("AllTalkVoice"))
{ /* re-select the voice from the AllTalk list */ } Prevention
- Filter the voice picker to voices owned by the active engine.
- Clear persisted mappings that reference other engines on engine switch.
- Guard the type at the call site so the error surfaces with the voice name.
When it happens
Trigger: A Voice whose EngineVoice is not AllTalkVoice is passed to AllTalk.Speak (e.g. an AzureVoice or MorpheusVoice selected while the AllTalk engine is the active one).
Common situations: Stale voice mapping persisted after the user switched engines; deserialized Voice lost its typed EngineVoice; cast-dialog race where the voice list and the active engine disagree.
Related errors
- Voice is not an AzureVoice
- Voice is not a ChatterboxVoice
- Voice is not a Qwen3TtsVoice
- Voice is not a VibeVoice
- Voice is not a CosyVoice3Voice
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/a127deb1db9d7029.
Report an issue: GitHub.