SubtitleEdit/subtitleedit · error · HttpRequestException
ElevenLabs voice list request failed: HTTP {(int)result.Stat
Error message
ElevenLabs voice list request failed: HTTP {(int)result.StatusCode} {result.StatusCode} What it means
HttpRequestException from DownloadElevenLabsVoiceList when the voice-list GET returns non-2xx. Per the comment, this throw-on-failure is intentional: copying an error body into the cached voice-list JSON used to permanently empty the list because the bundled fallback only restores when the file is missing.
Source
Thrown at src/ui/Logic/Download/TtsDownloadService.cs:230
requestMessage.Headers.TryAddWithoutValidation("Content-Type", "application/json");
requestMessage.Headers.TryAddWithoutValidation("Accept", "application/json");
if (!string.IsNullOrEmpty(Se.Settings.Video.TextToSpeech.ElevenLabsApiKey))
{
requestMessage.Headers.TryAddWithoutValidation("xi-api-key", Se.Settings.Video.TextToSpeech.ElevenLabsApiKey);
}
var result = await _httpClient.SendAsync(requestMessage, cancellationToken);
// Throw on failure instead of copying the error body into the stream: the caller writes
// the stream over its cached voice-list JSON, and since the bundled fallback only kicks
// in when the file is *missing*, one failed refresh (expired key, network error) used to
// permanently empty the voice list.
if (!result.IsSuccessStatusCode)
{
var error = (await result.Content.ReadAsStringAsync(cancellationToken)).Trim();
SeLogger.Error($"ElevenLabs TTS failed calling API address {url} : Status code={result.StatusCode} {TruncateForLog(error)}");
throw new HttpRequestException($"ElevenLabs voice list request failed: HTTP {(int)result.StatusCode} {result.StatusCode}");
}
await result.Content.CopyToAsync(ms, cancellationToken);
}
public async Task DownloadMurfVoiceList(MemoryStream ms, IProgress<float>? progress, CancellationToken cancellationToken)
{
var url = "https://api.murf.ai/v1/speech/voices";
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
requestMessage.Headers.TryAddWithoutValidation("Content-Type", "application/json");
requestMessage.Headers.TryAddWithoutValidation("Accept", "application/json");
requestMessage.Headers.TryAddWithoutValidation("api-key", Se.Settings.Video.TextToSpeech.MurfApiKey);
var result = await _httpClient.SendAsync(requestMessage, cancellationToken);
// See DownloadElevenLabsVoiceList: an error body must not reach the cached voice list.
if (!result.IsSuccessStatusCode)View on GitHub (pinned to 17a9f07487)
Solutions
- Verify the ElevenLabs API key is valid and not expired.
- Check the account quota / billing status (429 means throttle).
- Inspect the SeLogger line for the trimmed server error body.
- Retry after a short backoff for transient 5xx responses.
Defensive patterns
Strategy: try-catch
Validate before calling
if (string.IsNullOrWhiteSpace(Se.Settings.Video.TextToSpeech.ElevenLabsApiKey))
{
throw new InvalidOperationException("ElevenLabs API key is not set.");
} Try / catch
try { await service.DownloadElevenLabsVoiceList(ms, progress, ct); }
catch (HttpRequestException ex) when (ex.Message.Contains("ElevenLabs voice list"))
{
// keep the existing cached voice list; surface the status code to the user
} Prevention
- Do NOT overwrite the cached voice-list JSON on a failed refresh (the throw design enforces this).
- Verify the API key and quota before refreshing.
- Retry transient 5xx with backoff; treat 401/429 as config/quota issues.
When it happens
Trigger: _httpClient.SendAsync for the ElevenLabs voice list returns !IsSuccessStatusCode. The trimmed error body is logged, and the status code is surfaced in the exception message.
Common situations: Expired or invalid ElevenLabs API key (401), quota exhausted (429), wrong endpoint, or transient server error (5xx).
Related errors
- Murf voice list request failed: HTTP {(int)result.StatusCode
- Mistral voice list request failed: HTTP {(int)result.StatusC
- MOSS-TTS (CrispASR) request failed — the connection to the c
- OmniVoice (CrispASR) request failed — the connection to the
- AllTalk TTS server is not reachable. Please check that the s
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/502f1c36ca9b5434.
Report an issue: GitHub.