{"record":{"id":"a127deb1db9d7029","repo":"SubtitleEdit/subtitleedit","slug":"voice-is-not-a-alltalkvoice","errorCode":null,"errorMessage":"Voice is not a AllTalkVoice","messagePattern":"Voice is not a AllTalkVoice","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/AllTalk.cs","lineNumber":161,"sourceCode":"    {\n        var ms = new MemoryStream();\n        await _ttsDownloadService.DownloadAllTalkVoiceList(ms, null, cancellationToken);\n        await File.WriteAllBytesAsync(Path.Combine(GetSetAllTalkFolder(), JsonFileName), ms.ToArray(), cancellationToken);\n        return await GetVoices(language);\n    }\n\n    public async Task<TtsResult> Speak(\n        string text,\n        string outputFolder,\n        Voice voice,\n        TtsLanguage? language,\n        string? region,\n        string? model,\n        CancellationToken cancellationToken)\n    {\n        if (voice.EngineVoice is not AllTalkVoice allTalkVoice)\n        {\n            throw new ArgumentException(\"Voice is not a AllTalkVoice\");\n        }\n\n        var languageCode = language != null ? language.Code : \"en\";\n        Se.WriteToolsLog($\"AllTalk: voice={allTalkVoice.Voice}, language={languageCode}, textLen={text.Length}\");\n        var allTalkFileNameOutputFileName = await _ttsDownloadService.AllTalkVoiceSpeak(text, allTalkVoice, languageCode, cancellationToken);\n\n        // The server reports its output as a local path. It can be empty (unexpected response\n        // JSON) or unreachable from here (server on another machine / different working dir) -\n        // File.Move then threw unhandled instead of failing the segment like other engines.\n        if (string.IsNullOrWhiteSpace(allTalkFileNameOutputFileName) || !File.Exists(allTalkFileNameOutputFileName))\n        {\n            var error = $\"AllTalk did not produce a readable output file (reported path: \\\"{allTalkFileNameOutputFileName}\\\") - is the server running on this machine?\";\n            Se.WriteToolsLog(\"AllTalk: \" + error, true);\n            return new TtsResult { Text = text, FileName = string.Empty, Error = true, ErrorMessage = error };\n        }\n\n        var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetAllTalkFolder), Guid.NewGuid() + \".wav\");\n        File.Move(allTalkFileNameOutputFileName, outputFileName);","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/AllTalk.cs#L143-L179","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: pass any voice\nawait _allTalk.Speak(text, out, voice, lang, region, model, ct);\n// after: guard at the call site\nif (voice.EngineVoice is not AllTalkVoice)\n    throw new InvalidOperationException($\"Voice {voice.Name} is not an AllTalk voice; re-select it.\");\nawait _allTalk.Speak(text, out, voice, lang, region, model, ct);","handlingStrategy":"type-guard","validationCode":"if (voice.EngineVoice is not AllTalkVoice) return Invalid($\"Voice {voice.Name} is not an AllTalk voice\");","typeGuard":"static bool IsAllTalkVoice(Voice v) => v.EngineVoice is AllTalkVoice;","tryCatchPattern":"try { await allTalk.Speak(...); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"AllTalkVoice\"))\n{ /* re-select the voice from the AllTalk list */ }","preventionTips":["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."],"tags":["tts","voice","type-mismatch","alltalk","text-to-speech"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}