{"record":{"id":"87d5283aabaf858b","repo":"SubtitleEdit/subtitleedit","slug":"murf-voice-list-request-failed-http-int-result","errorCode":null,"errorMessage":"Murf voice list request failed: HTTP {(int)result.StatusCode} {result.StatusCode}","messagePattern":"Murf voice list request failed: HTTP (.+?) (.+?)","errorType":"exception","errorClass":"HttpRequestException","httpStatus":null,"severity":"error","filePath":"src/ui/Logic/Download/TtsDownloadService.cs","lineNumber":252,"sourceCode":"    }\n\n    public async Task DownloadMurfVoiceList(MemoryStream ms, IProgress<float>? progress, CancellationToken cancellationToken)\n    {\n        var url = \"https://api.murf.ai/v1/speech/voices\";\n\n        using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);\n        requestMessage.Headers.TryAddWithoutValidation(\"Content-Type\", \"application/json\");\n        requestMessage.Headers.TryAddWithoutValidation(\"Accept\", \"application/json\");\n        requestMessage.Headers.TryAddWithoutValidation(\"api-key\", Se.Settings.Video.TextToSpeech.MurfApiKey);\n\n        var result = await _httpClient.SendAsync(requestMessage, cancellationToken);\n\n        // See DownloadElevenLabsVoiceList: an error body must not reach the cached voice list.\n        if (!result.IsSuccessStatusCode)\n        {\n            var error = (await result.Content.ReadAsStringAsync(cancellationToken)).Trim();\n            SeLogger.Error($\"Murf TTS failed calling API address {url} : Status code={result.StatusCode} {TruncateForLog(error)}\");\n            throw new HttpRequestException($\"Murf voice list request failed: HTTP {(int)result.StatusCode} {result.StatusCode}\");\n        }\n\n        await result.Content.CopyToAsync(ms, cancellationToken);\n    }\n\n    public async Task DownloadAzureVoiceList(Stream stream, IProgress<float>? progress, CancellationToken cancellationToken)\n    {\n        // Azure's official voice-list endpoint (the previous URL pointed at the ElevenLabs API,\n        // whose response Azure's parser cannot read). Requires the user's region + subscription\n        // key; the response is a JSON array with DisplayName/ShortName/Gender/Locale fields -\n        // the exact shape AzureSpeech.Map parses. Throws on failure so a refresh cannot\n        // overwrite the cached voice list with an error body.\n        var region = Se.Settings.Video.TextToSpeech.AzureRegion;\n        if (string.IsNullOrWhiteSpace(region))\n        {\n            throw new InvalidOperationException(\"Azure region is not set - enter it in the TTS engine settings before refreshing voices.\");\n        }\n","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Logic/Download/TtsDownloadService.cs#L234-L270","documentation":"HttpRequestException from DownloadMurfVoiceList on a non-2xx response, mirroring the ElevenLabs design. The comment notes an error body must never reach the cached voice list, so the call throws instead of writing an error payload to the cache.","triggerScenarios":"GET https://api.murf.ai/v1/speech/voices with the user's MurfApiKey returns !IsSuccessStatusCode. Trimmed body logged, status code in the thrown message.","commonSituations":"Invalid/expired Murf API key (401), quota hit (429), wrong key in MurfApiKey setting, or a Murr-side outage (5xx).","solutions":["Confirm MurfApiKey in settings is current and valid.","Check Murf account quota and plan limits.","Read the trimmed error in SeLogger for the server's reason.","Retry on transient 5xx after backoff."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (string.IsNullOrWhiteSpace(Se.Settings.Video.TextToSpeech.MurfApiKey))\n{\n    throw new InvalidOperationException(\"Murf API key is not set.\");\n}","typeGuard":null,"tryCatchPattern":"try { await service.DownloadMurfVoiceList(ms, progress, ct); }\ncatch (HttpRequestException ex) when (ex.Message.Contains(\"Murf voice list\"))\n{\n    // preserve the cached voice list; show the status code to the user\n}","preventionTips":["Never write an error body into the cached voice-list file (the throw enforces this).","Confirm MurfApiKey validity and plan quota before refresh.","Retry transient 5xx with bounded backoff."],"tags":["network","http","tts","murf","voice-list","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}