SubtitleEdit/subtitleedit · error · HttpRequestException

Mistral voice list request failed: HTTP {(int)result.StatusC

Error message

Mistral voice list request failed: HTTP {(int)result.StatusCode} {result.StatusCode}

What it means

HttpRequestException from DownloadMistralVoiceList on a non-2xx response. Per the comment, throwing (rather than returning an empty stream) prevents a silently failed refresh from permanently emptying the cached voice list, since the bundled fallback only restores when the file is missing.

Source

Thrown at src/ui/Logic/Download/TtsDownloadService.cs:776

    public async Task DownloadMistralSpeechVoiceList(MemoryStream ms, IProgress<float>? progress, CancellationToken cancellationToken)
    {
        var url = "https://api.mistral.ai/v1/audio/voices?limit=10000";

        using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
        requestMessage.Headers.TryAddWithoutValidation("Content-Type", "application/json");
        requestMessage.Headers.TryAddWithoutValidation("Accept", "application/json");
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Se.Settings.Video.TextToSpeech.MistralApiKey);

        var result = await _httpClient.SendAsync(requestMessage, cancellationToken);

        // Throw instead of returning with an empty stream: the caller writes the stream over its
        // cached voice-list JSON, so a silently failed refresh permanently emptied the voice list
        // (the bundled fallback only restores when the file is missing).
        if (!result.IsSuccessStatusCode)
        {
            SeLogger.Error($"Mistral TTS failed calling API address {url} : Status code={result.StatusCode}");
            throw new HttpRequestException($"Mistral voice list request failed: HTTP {(int)result.StatusCode} {result.StatusCode}");
        }

        await result.Content.CopyToAsync(ms, cancellationToken);
    }

    public async Task<bool> DownloadMistralSpeechSpeak(
        string inputText,
        MistralVoice voice,
        string model,
        string apiKey,
        MemoryStream stream,
        IProgress<float>? progress,
        CancellationToken cancellationToken)
    {
        var url = "https://api.mistral.ai/v1/audio/speech";
        var text = Utilities.UnbreakLine(inputText);

        var data = "{ \"model\": \"" + Json.EncodeJsonText(model) +

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Verify MistralApiKey is valid and not expired.
  2. Check Mistral quota/plan limits (429).
  3. Inspect the SeLogger status line for the server reason.
  4. Retry transient 5xx after backoff.
Defensive patterns

Strategy: try-catch

Validate before calling

if (string.IsNullOrWhiteSpace(Se.Settings.Video.TextToSpeech.MistralApiKey))
{
    throw new InvalidOperationException("Mistral API key is not set.");
}

Try / catch

try { await service.DownloadMistralVoiceList(ms, progress, ct); }
catch (HttpRequestException ex) when (ex.Message.Contains("Mistral voice list"))
{
    // keep cached voice list; show the status code to the user
}

Prevention

When it happens

Trigger: _httpClient.SendAsync with Bearer MistralApiKey returns !IsSuccessStatusCode. Status logged; status code surfaced in the thrown message.

Common situations: Invalid/expired MistralApiKey (401), quota/throttle (429), wrong endpoint, or Mistral-side outage (5xx).

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/773faf14f8a51601. Report an issue: GitHub.