SubtitleEdit/subtitleedit · error · HttpRequestException
STT request failed with status {statusCode} ({response.Statu
Error message
STT request failed with status {statusCode} ({response.StatusCode}) calling {safeEndpoint}. Response: {errorContent} What it means
The OpenAI-compatible STT endpoint returned a non-2xx status. The status code is carried on the HttpRequestException (via response.StatusCode) so callers can branch on it — notably a 4xx complaining about 'model' is turned into a Model-field hint (issue #12877). The endpoint is sanitized for logging via SanitizeEndpointForLog before being written to the message or tools log.
Source
Thrown at src/ui/Features/Video/SpeechToText/OpenAiCompatible/OpenAiSttService.cs:229
? _settings.Temperature.ToString("F2", CultureInfo.InvariantCulture)
: "(not sent)";
var granularitiesSummary = _settings.Stream ? "(not sent)" : "[segment,word]";
var paramSummary =
$"model={_settings.Model}, language={languageToUse}, " +
$"response_format={responseFormat}, timestamp_granularities={granularitiesSummary}, " +
$"stream={(_settings.Stream ? "true" : "(not sent)")}, " +
$"temperature={temperatureSummary}, " +
$"promptLen={_settings.Prompt?.Length ?? 0}, file={fileName}";
var safeEndpoint = SanitizeEndpointForLog(_settings.EndpointUrl);
_settings.Logger?.Invoke(
$"OpenAI-compatible STT failed: POST {safeEndpoint}{Environment.NewLine}" +
$"Status: {statusCode} {response.StatusCode}{Environment.NewLine}" +
$"RequestParams: {paramSummary}{Environment.NewLine}" +
$"ResponseBody: {errorContent}");
// Carry the status code on the exception: the caller turns a 4xx that
// complains about "model" into a hint about the Model field, and
// sniffing the number back out of the message is fragile (issue #12877).
throw new HttpRequestException(
$"STT request failed with status {statusCode} ({response.StatusCode}) " +
$"calling {safeEndpoint}. Response: {errorContent}",
null,
response.StatusCode);
}
// Check if streaming (SSE)
var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
if (contentType.Contains("text/event-stream") || response.Headers.Contains("Server-Sent-Events"))
{
return await ParseSseStreamAsync(response, progress, segmentProgress, cancellationToken);
}
// Non-streaming response
var jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
return ParseJsonResponse(jsonResponse);
}
View on GitHub (pinned to 17a9f07487)
Solutions
- Read statusCode and errorContent in the message — a 4xx mentioning 'model' usually means the Model field is wrong (issue #12877).
- Confirm EndpointUrl is the STT transcription endpoint, not the chat endpoint.
- Validate/rotate the API key.
- Back off and retry on 429 rate limits.
Example fix
// before: chat endpoint used for STT EndpointUrl = "https://api.example.com/v1/chat/completions"; // after: correct transcription endpoint EndpointUrl = "https://api.example.com/v1/audio/transcriptions";
Defensive patterns
Strategy: try-catch
Validate before calling
if (!Uri.TryCreate(_settings.EndpointUrl, UriKind.Absolute, out var uri)
|| !uri.AbsolutePath.EndsWith("/transcriptions", StringComparison.OrdinalIgnoreCase))
return Invalid("EndpointUrl must be the /v1/audio/transcriptions URL"); Try / catch
try { return await service.TranscribeAsync(...); }
catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.BadRequest && ex.Message.Contains("model"))
{ /* hint: Model field is wrong (issue #12877) */ }
catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Unauthorized)
{ /* API key invalid */ } Prevention
- Point EndpointUrl at the transcription endpoint, never chat/completions.
- Keep the status code on the exception so callers can branch (as this engine does).
- Sanitize endpoints in logs to avoid leaking keys in URLs.
When it happens
Trigger: Any non-success HTTP status from POST {EndpointUrl}: 400/404 about model name, 401 bad API key, 403 forbidden, 404 wrong endpoint path, 429 rate limit, 5xx server error.
Common situations: EndpointUrl points at /v1/chat/completions instead of /v1/audio/transcriptions; wrong or non-existent Model name; expired/invalid API key; rate-limited account; provider outage.
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 timed out after {_settings.TimeoutSeconds} secon
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/732f33f9cc5ae3a6.
Report an issue: GitHub.