{"record":{"id":"23df821b75422af6","repo":"SubtitleEdit/subtitleedit","slug":"stt-request-timed-out-after-settings-timeoutseco","errorCode":null,"errorMessage":"STT request timed out after {_settings.TimeoutSeconds} seconds.","messagePattern":"STT request timed out after (.+?) seconds\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/SpeechToText/OpenAiCompatible/OpenAiSttService.cs","lineNumber":107,"sourceCode":"        CancellationToken cancellationToken = default)\n    {\n        // Apply the per-call deadline via a linked CTS rather than the shared\n        // HttpClient.Timeout, so the shared client stays unmodified.\n        using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n        if (_settings.TimeoutSeconds > 0)\n        {\n            timeoutCts.CancelAfter(TimeSpan.FromSeconds(_settings.TimeoutSeconds));\n        }\n\n        try\n        {\n            return await TranscribeCoreAsync(audioStream, fileName, language, progress, segmentProgress, timeoutCts.Token);\n        }\n        catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)\n        {\n            // Our own timeout fired, not a user cancel — surface it as an error\n            // so the caller doesn't mistake it for cancellation.\n            throw new TimeoutException($\"STT request timed out after {_settings.TimeoutSeconds} seconds.\");\n        }\n    }\n\n    private async Task<OpenAiCompatibleSttResponse> TranscribeCoreAsync(\n        Stream audioStream,\n        string fileName,\n        string? language,\n        IProgress<string>? progress,\n        IProgress<OpenAiCompatibleSegment>? segmentProgress,\n        CancellationToken cancellationToken)\n    {\n        using var content = new MultipartFormDataContent();\n\n        // Content-Type, upload filename extension, and bytes must all agree —\n        // OpenAI rejects e.g. webm-Opus bytes sent as audio/wav. The on-disk\n        // extension is the source of truth: the ViewModel's 16 kHz WAV short-\n        // circuit can hand us a real .wav even when the user picked \"mp3\" in\n        // settings. Fall back to the configured AudioFormat only when the","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/SpeechToText/OpenAiCompatible/OpenAiSttService.cs#L89-L125","documentation":"The engine's own timeoutCTS fires (CancelAfter TimeoutSeconds) before the OpenAI-compatible server returns, and it is provably not a user-initiated cancel (the `when (!cancellationToken.IsCancellationRequested)` guard). The OperationCanceledException is rewrapped as TimeoutException so callers cannot mistake it for the user cancelling the job.","triggerScenarios":"TranscribeCoreAsync (POST to EndpointUrl) runs longer than _settings.TimeoutSeconds while the caller's cancellationToken is still alive. The internal CTS is the one that cancelled.","commonSituations":"Slow/overloaded remote endpoint; large audio file uploaded with streaming disabled; endpoint behind a high-latency proxy; TimeoutSeconds configured too low in engine settings; network stall mid-upload.","solutions":["Raise _settings.TimeoutSeconds in the engine settings.","Verify endpoint reachability and latency with a direct curl/POST of the same audio.","Reduce per-request audio size (chunk the audio) or enable streaming.","Point at a closer/faster provider or a self-hosted model."],"exampleFix":"// before: default short timeout\n_settings.TimeoutSeconds = 30;\n// after: sized to real upload + inference time for large files\n_settings.TimeoutSeconds = 180;","handlingStrategy":"retry","validationCode":"if (_settings.TimeoutSeconds < estimatedUploadPlusInferSeconds(audioStream))\n    _settings.TimeoutSeconds = estimatedUploadPlusInferSeconds(audioStream);","typeGuard":null,"tryCatchPattern":"try { return await service.TranscribeAsync(...); }\ncatch (TimeoutException ex) when (ex.Message.Contains(\"STT request timed out\"))\n{ /* increase TimeoutSeconds, then retry once */ }","preventionTips":["Size TimeoutSeconds to upload bandwidth + model inference time, not a generic default.","Chunk large audio so each request finishes well under the timeout.","Distinguish this TimeoutException from the caller's cancellation before retrying."],"tags":["openai-stt","timeout","network","speech-to-text"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}