{"record":{"id":"0fac2dbfb09dae7f","repo":"SubtitleEdit/subtitleedit","slug":"vibevoice-crispasr-synthesis-failed-int-respo","errorCode":null,"errorMessage":"VibeVoice (CrispASR) synthesis failed ({(int)response.StatusCode}): {errorBody}{Environment.NewLine}Server log:{Environment.NewLine}{serverLog}{LaunchCmdSuffix}","messagePattern":"VibeVoice \\(CrispASR\\) synthesis failed \\((.+?)\\): (.+?)(.+?)Server log:(.+?)(.+?)(.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/TextToSpeech/Engines/VibeVoiceCrispAsr.cs","lineNumber":433,"sourceCode":"                + (string.IsNullOrEmpty(serverLog) ? string.Empty : $\"{Environment.NewLine}Server log:{Environment.NewLine}{serverLog}\")\n                + LaunchCmdSuffix(launchCommand),\n                ex);\n        }\n\n        using (response)\n        {\n            if (!response.IsSuccessStatusCode)\n            {\n                var errorBody = await SafeReadErrorAsync(response, cancellationToken);\n                var serverLog = SnapshotServerLog();\n                var launchCommand = _serverLaunchCommand;\n                var errMsg = $\"VibeVoice (CrispASR) server error {(int)response.StatusCode} {response.StatusCode} — \"\n                    + $\"Voice: {vibeVoice}, Text: {text}, RequestJson: {body}, \"\n                    + $\"ResponseBody: {errorBody}, ServerLog: {serverLog}\"\n                    + LaunchCmdSuffix(launchCommand);\n                Se.LogError(errMsg);\n                Se.WriteToolsLog(errMsg);\n                throw new InvalidOperationException(\n                    $\"VibeVoice (CrispASR) synthesis failed ({(int)response.StatusCode}): {errorBody}\"\n                    + (string.IsNullOrEmpty(serverLog) ? string.Empty : $\"{Environment.NewLine}Server log:{Environment.NewLine}{serverLog}\")\n                    + LaunchCmdSuffix(launchCommand));\n            }\n\n            await using var fileStream = File.Create(outputFileName);\n            await using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);\n            await contentStream.CopyToAsync(fileStream, cancellationToken);\n        }\n\n        return new TtsResult(outputFileName, text);\n    }\n\n    private static string FormatLaunchCommand(string exe, System.Collections.ObjectModel.Collection<string> args)\n    {\n        static string Quote(string s) =>\n            !string.IsNullOrEmpty(s) && s.IndexOfAny(new[] { ' ', '\\t' }) >= 0\n                ? \"\\\"\" + s.Replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\"","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/TextToSpeech/Engines/VibeVoiceCrispAsr.cs#L415-L451","documentation":"InvalidOperationException thrown when the VibeVoice /v1/audio/speech endpoint returns a non-success status. The response body is read via SafeReadErrorAsync and embedded along with the server log and launch command. Detailed context (voice, text, request JSON, response body) is logged via Se.LogError before the throw.","triggerScenarios":"HTTP 400 because `voice` contained a path separator/extension (server's path-traversal guard); HTTP 400 for an out-of-range `speed` (clamped client-side, but a fork may bypass); HTTP 500 when the reference WAV under --voice-dir is missing or the wrong format; HTTP 404 if the route was renamed in a newer crispasr build.","commonSituations":"Sending the full FilePath instead of the bare stem (the code already sends the stem, but a regression can revert it); reference WAV not 24 kHz mono; voice cloning attestations rejected by a build that gates cloning; route drift between crispasr versions.","solutions":["Read the embedded errorBody — the server states the exact rejected field.","Confirm `voice` is Path.GetFileNameWithoutExtension(FilePath) — never the full path and never with .wav/.gguf.","Re-import the reference as a 24 kHz mono WAV if the server rejects the audio format.","If cloning attestations are required by this build, ensure CrispAsrTtsProvenance.AddSpeechAttestations is invoked and the user accepted cloning in settings."],"exampleFix":"// before — full path triggers the path-traversal guard (HTTP 400)\npayload[\"voice\"] = vibeVoice.FilePath;\n\n// after — bare stem only\npayload[\"voice\"] = Path.GetFileNameWithoutExtension(vibeVoice.FilePath);","handlingStrategy":"validation","validationCode":"// Always send the bare stem and a clamped speed.\npayload[\"voice\"] = Path.GetFileNameWithoutExtension(vibeVoice.FilePath);\npayload[\"speed\"] = Math.Clamp(Se.Settings.Video.TextToSpeech.VibeVoiceCrispAsrSpeed, 0.25, 4.0);\nCrispAsrTtsProvenance.AddSpeechAttestations(payload);","typeGuard":null,"tryCatchPattern":"try { response = await HttpClient.PostAsync(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"synthesis failed\"))\n{ Se.LogError(ex); throw; }","preventionTips":["Send only the extension-less stem as `voice`.","Keep the reference WAV at 24 kHz mono.","Ensure cloning attestations are attached when the build requires them."],"tags":["tts","vibevoice","crispasr","http-status","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}