{"record":{"id":"811c90e2d6ad9e85","repo":"SubtitleEdit/subtitleedit","slug":"voice-is-not-an-edgettsvoice","errorCode":null,"errorMessage":"Voice is not an EdgeTtsVoice","messagePattern":"Voice is not an EdgeTtsVoice","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/EdgeTts.cs","lineNumber":122,"sourceCode":"        {\n            voices = GetFallbackVoices();\n        }\n\n        return voices.Select(v => new Voice(v)).ToArray();\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 EdgeTtsVoice edgeVoice)\n        {\n            throw new ArgumentException(\"Voice is not an EdgeTtsVoice\");\n        }\n\n        var folder = TtsOutputFolder.Resolve(outputFolder, GetSetEdgeTtsFolder);\n        var fileName = Path.Combine(folder, Guid.NewGuid() + \".mp3\");\n        var escapedText = EscapeQuotedArgValue(text);\n        var escapedVoice = EscapeQuotedArgValue(edgeVoice.Name);\n        var args =\n            $\"--voice \\\"{escapedVoice}\\\" --text \\\"{escapedText}\\\" --write-media \\\"{fileName}\\\"\";\n\n        var rate = NormalizeProsodyValue(Se.Settings.Video.TextToSpeech.EdgeTtsRate, \"%\");\n        if (!string.IsNullOrEmpty(rate))\n        {\n            args += $\" --rate={rate}\";\n        }\n\n        var pitch = NormalizeProsodyValue(Se.Settings.Video.TextToSpeech.EdgeTtsPitch, \"Hz\");\n        if (!string.IsNullOrEmpty(pitch))\n        {","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/EdgeTts.cs#L104-L140","documentation":"ArgumentException from EdgeTts.Speak: the passed Voice's EngineVoice is not an EdgeTtsVoice. EdgeTts pattern-matches its concrete voice type (which carries the edge-tts voice Name) and refuses any other engine's voice. The message is literal and non-negotiable.","triggerScenarios":"Calling edgeTtsEngine.Speak(...) with a Voice whose EngineVoice is a CosyVoice3Voice, ElevenLabVoice, F5TtsVoice, ChatterboxVoice, etc.; a dispatch bug routing the wrong voice; deserialised voice rehydrated to the wrong subtype.","commonSituations":"Cast row using a globally-selected voice from a different engine; refactor that changed EngineVoice typing; voice JSON rehydration picking the wrong concrete type.","solutions":["Pass only voices produced by EdgeTts.GetVoices/RefreshVoices into EdgeTts.Speak.","Dispatch each Voice to its owning engine by inspecting EngineVoice's type.","Add a test asserting every cast voice maps to the engine that created it."],"exampleFix":"// before\nawait edgeTtsEngine.Speak(text, out, cosyVoice, lang, region, model, ct);\n\n// after\nif (voice.EngineVoice is not EdgeTtsVoice)\n    throw new InvalidOperationException($\"EdgeTts cannot speak {voice.EngineVoice.GetType().Name}\");\nawait edgeTtsEngine.Speak(text, out, voice, lang, region, model, ct);","handlingStrategy":"type-guard","validationCode":"if (voice.EngineVoice is not EdgeTtsVoice) {\n    Se.WriteToolsLog($\"Skipping EdgeTts synthesis: voice is {voice.EngineVoice?.GetType().Name}\", true);\n    return;\n}","typeGuard":"static bool IsEdgeTtsVoice(Voice voice) => voice?.EngineVoice is EdgeTtsVoice;","tryCatchPattern":"try { await edgeTtsEngine.Speak(text, out, voice, lang, region, model, ct); }\ncatch (ArgumentException ex) when (ex.Message == \"Voice is not an EdgeTtsVoice\")\n{ Se.WriteToolsLog($\"Voice/engine mismatch for EdgeTts: {voice.EngineVoice?.GetType().Name}\", true); }","preventionTips":["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 EdgeTts."],"tags":["tts","edge-tts","type-mismatch","voice-dispatch","argument-exception"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}