SubtitleEdit/subtitleedit · error · TimeoutException

OpenRouter transcription timed out after {_settings.TimeoutS

Error message

OpenRouter transcription timed out after {_settings.TimeoutSeconds} seconds.

What it means

Same timeout pattern as the OpenAI-compatible engine but for OpenRouter: the internal timeoutCTS (CancelAfter TimeoutSeconds) fires before TranscribeCoreAsync returns, and it is not a user cancel. Wrapped as TimeoutException so it is not mistaken for caller cancellation.

Source

Thrown at src/ui/Features/Video/SpeechToText/OpenRouter/OpenRouterSttService.cs:83

        string? language,
        CancellationToken cancellationToken)
    {
        using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        if (_settings.TimeoutSeconds > 0)
        {
            timeoutCts.CancelAfter(TimeSpan.FromSeconds(_settings.TimeoutSeconds));
        }
        var ct = timeoutCts.Token;

        try
        {
            return await TranscribeCoreAsync(audioBytes, format, language, ct);
        }
        catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
        {
            // Our own timeout fired, not a user cancel — surface it as an error
            // so the caller doesn't mistake it for cancellation.
            throw new TimeoutException($"OpenRouter transcription timed out after {_settings.TimeoutSeconds} seconds.");
        }
    }

    private async Task<OpenAiCompatibleSttResponse> TranscribeCoreAsync(
        byte[] audioBytes,
        string format,
        string? language,
        CancellationToken cancellationToken)
    {
        var body = BuildRequestBody(_settings, audioBytes, format, language);
        using var content = new StringContent(body, Encoding.UTF8, "application/json");
        using var request = new HttpRequestMessage(HttpMethod.Post, _settings.EndpointUrl)
        {
            Content = content,
        };

        if (!string.IsNullOrEmpty(_settings.ApiKey))
        {

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Increase _settings.TimeoutSeconds for OpenRouter.
  2. Pick a faster underlying model in OpenRouter settings.
  3. Verify network path to OpenRouter.
  4. Shorten the audio clip per request.

Example fix

// before
_settings.TimeoutSeconds = 60;
// after: base64-encoded body + remote inference needs more headroom
_settings.TimeoutSeconds = 240;
Defensive patterns

Strategy: retry

Validate before calling

if (_settings.TimeoutSeconds < 120) _settings.TimeoutSeconds = 120; // base64 body + remote infer

Try / catch

try { return await openRouter.TranscribeAsync(...); }
catch (TimeoutException ex) when (ex.Message.Contains("OpenRouter transcription timed out"))
{ /* raise TimeoutSeconds or pick a faster model, then retry */ }

Prevention

When it happens

Trigger: OpenRouter TranscribeCoreAsync (POST _settings.EndpointUrl with base64 audio body) exceeds _settings.TimeoutSeconds while the caller's token is still active.

Common situations: OpenRouter routing to a slow underlying model; large base64 body inflating upload time; TimeoutSeconds too low for the chosen model; network stall.

Understand the failure class

Related errors


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