SubtitleEdit/subtitleedit · error · HttpRequestException
OpenRouter STT request failed with status {statusCode} ({res
Error message
OpenRouter STT request failed with status {statusCode} ({response.StatusCode}). Response: {errorContent} What it means
OpenRouter STT endpoint returned non-2xx; the status code and response body are embedded in the message. Note the log line writes _settings.EndpointUrl RAW (unlike the OpenAI path which sanitizes), and the thrown message itself does not include the endpoint.
Source
Thrown at src/ui/Features/Video/SpeechToText/OpenRouter/OpenRouterSttService.cs:119
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _settings.ApiKey);
}
// OpenRouter uses these to attribute traffic; harmless if the server ignores them.
request.Headers.TryAddWithoutValidation("HTTP-Referer", "https://www.nikse.dk/subtitleedit");
request.Headers.TryAddWithoutValidation("X-Title", "Subtitle Edit");
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync(cancellationToken);
var statusCode = (int)response.StatusCode;
_settings.Logger?.Invoke(
$"OpenRouter STT failed: POST {_settings.EndpointUrl}{Environment.NewLine}" +
$"Status: {statusCode} {response.StatusCode}{Environment.NewLine}" +
$"RequestParams: model={_settings.Model}, language={language ?? _settings.Language}, format={format}, bytes={audioBytes.Length}{Environment.NewLine}" +
$"ResponseBody: {errorContent}");
throw new HttpRequestException(
$"OpenRouter STT request failed with status {statusCode} ({response.StatusCode}). Response: {errorContent}");
}
var json = await response.Content.ReadAsStringAsync(cancellationToken);
return ParseResponse(json);
}
/// <summary>
/// Serialize the OpenRouter transcription request body. The audio is
/// base64-encoded (raw, not a data URI) under <c>input_audio</c>, and
/// <c>verbose_json</c> + <c>timestamp_granularities[]</c> ask for segment and
/// word timings when the underlying model supports them.
/// </summary>
internal static string BuildRequestBody(OpenRouterSttSettings settings, byte[] audioBytes, string format, string? language)
{
using var stream = new MemoryStream();
using (var writer = new Utf8JsonWriter(stream))
{View on GitHub (pinned to 17a9f07487)
Solutions
- Read statusCode and errorContent in the message to see the upstream reason.
- Confirm _settings.Model is a valid OpenRouter model id that supports audio.
- Validate/rotate the API key.
- Retry with backoff on 429.
Example fix
// before: model id OpenRouter rejects Model = "whisper-1"; // after: a model id OpenRouter actually routes for audio Model = "openai/whisper-large-v3";
Defensive patterns
Strategy: try-catch
Validate before calling
if (string.IsNullOrWhiteSpace(_settings.Model) || !_settings.Model.Contains('/'))
return Invalid("OpenRouter Model must be a provider/model id"); Try / catch
try { return await openRouter.TranscribeAsync(...); }
catch (HttpRequestException ex) when (ex.Message.Contains("OpenRouter STT request failed"))
{ /* parse statusCode from message; 401 -> key, 404 -> model/endpoint, 429 -> backoff */ } Prevention
- Use a valid OpenRouter provider/model id for audio.
- Note this engine logs EndpointUrl raw — do not put secrets in the URL.
- Branch on status code rather than substring-matching the message.
When it happens
Trigger: Non-success status from POST _settings.EndpointUrl: 400 bad model/format, 401 bad API key, 404 wrong endpoint, 429 rate limit, 5xx from OpenRouter or the routed upstream model.
Common situations: Wrong _settings.Model name; invalid/expired OpenRouter API key; endpoint URL misconfigured; rate limit on the account; the routed upstream provider returned an error OpenRouter forwards.
Related errors
- DashScope upload-policy request failed ({(int)policyResponse
- DashScope OSS upload failed ({(int)uploadResponse.StatusCode
- DashScope async submit failed ({(int)response.StatusCode}).
- DashScope task poll failed ({(int)response.StatusCode}). Res
- STT request failed with status {statusCode} ({response.Statu
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/029438e0251f1d51.
Report an issue: GitHub.