{"record":{"id":"7da1c5994283d4c4","repo":"SubtitleEdit/subtitleedit","slug":"voice-is-not-a-vibevoice","errorCode":null,"errorMessage":"Voice is not a VibeVoice","messagePattern":"Voice is not a VibeVoice","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/VibeVoiceCrispAsr.cs","lineNumber":340,"sourceCode":"    public Task<string[]> GetModels() => Task.FromResult(new[] { ModelKeyQ4K, ModelKeyQ8_0, ModelKeyF16 });\n\n    public Task<TtsLanguage[]> GetLanguages(Voice voice, string? model) => Task.FromResult(Array.Empty<TtsLanguage>());\n\n    public Task<Voice[]> RefreshVoices(string language, CancellationToken cancellationToken) =>\n        GetVoices(language);\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 VibeVoice vibeVoice)\n        {\n            throw new ArgumentException(\"Voice is not a VibeVoice\");\n        }\n\n        if (string.IsNullOrEmpty(vibeVoice.FilePath))\n        {\n            throw new InvalidOperationException(\n                \"VibeVoice (CrispASR) requires a reference voice WAV. \"\n                + \"Import one via the voice settings, then pick it in the voice combo. \"\n                + \"Reference WAV should be 24 kHz mono.\");\n        }\n\n        var modelKey = ResolveModelKey(model);\n        await EnsureServerRunningAsync(modelKey, cancellationToken);\n\n        var outputFileName = Path.Combine(TtsOutputFolder.Resolve(outputFolder, GetSetFolder), Guid.NewGuid() + \".wav\");\n        var inputText = text;\n\n        // OpenAI-compatible /v1/audio/speech payload. CrispASR's vibevoice backends look at:\n        //   - `input`             — the text to synthesise","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/VibeVoiceCrispAsr.cs#L322-L358","documentation":"ArgumentException thrown at the top of VibeVoiceCrispAsr.Speak when voice.EngineVoice is not a VibeVoice. VibeVoiceCrispAsr only does voice cloning and its voice list is populated exclusively with Voice(new VibeVoice(...)); passing any other engine's voice is a contract violation.","triggerScenarios":"A voice from Qwen3TtsCpp, Qwen3TtsCrispAsr, or a cloud TTS engine was handed to this engine's Speak; engine-switch race left the previous engine's voice selected; tests share a voice fixture across engines.","commonSituations":"UI engine switch that didn't re-resolve the selected voice; deserialized Voice that lost its subtype; cross-engine test fixtures.","solutions":["Pass only voices obtained from VibeVoiceCrispAsr.GetVoices into its Speak.","Clear/re-resolve the selected voice on engine switch.","Filter the voice combo in the UI so non-VibeVoice entries can't be selected for this engine.","In tests, build voices as `new Voice(new VibeVoice(...))`."],"exampleFix":"// before\nawait vibeEngine.Speak(text, folder, qwen3Voice, ...);\n\n// after — resolve a VibeVoice from this engine\nvar voices = await vibeEngine.GetVoices(language);\nvar voice = voices.Single(v => ((VibeVoice)v.EngineVoice).Voice == desiredName);\nawait vibeEngine.Speak(text, folder, voice, ...);","handlingStrategy":"type-guard","validationCode":"if (voice.EngineVoice is not VibeVoice)\n    throw new ArgumentException(\"Voice is not a VibeVoice\");","typeGuard":"static bool IsVibeVoice(Voice v) => v.EngineVoice is VibeVoice;","tryCatchPattern":null,"preventionTips":["Resolve voices from VibeVoiceCrispAsr.GetVoices only.","Clear the selected voice on engine switch.","Filter the combo by VibeVoice subtype."],"tags":["tts","vibevoice","voice","type-mismatch","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}