From 96dd1bfe9e45d8c3a13357b862c0990c928c3e62 Mon Sep 17 00:00:00 2001 From: brunorezio Date: Sun, 26 Jul 2026 05:07:33 +0100 Subject: [PATCH] disable Bun's fetch timeout when calling whisper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bun's fetch aborts at 300s by default. whisper.cpp runs at roughly 8x realtime, so a 43-minute recording needs about 5.4 minutes and died with "The operation timed out" some 20 seconds short of the answer — long enough to look like a slow machine rather than a ceiling. AbortSignal.timeout() does not raise that ceiling; only `timeout: false` does, which the DOM RequestInit type does not declare, hence the local BunRequestInit. Verified end to end: a 43-minute episode through the task now completes in 547s with a 27k-character transcript, where it previously failed at exactly 300s. Co-Authored-By: Claude Opus 5 --- src/servers/api/stt/transcribe.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/servers/api/stt/transcribe.ts b/src/servers/api/stt/transcribe.ts index 4b58f2a5..34e5a3a9 100644 --- a/src/servers/api/stt/transcribe.ts +++ b/src/servers/api/stt/transcribe.ts @@ -6,6 +6,16 @@ // // The detection must win when Whisper is confident. `spokenLanguages` only breaks ties on clips // Whisper is genuinely unsure about, where its low-confidence guess defaults to English. +// +// Both requests pass `timeout: false`. Bun's fetch aborts at 300s by default, and whisper.cpp runs at +// roughly 8x realtime, so anything over ~40 minutes of audio died with "The operation timed out" +// about 20 seconds short of the answer. AbortSignal.timeout() does not raise that ceiling — only +// disabling the built-in timeout does. + +// Bun's fetch accepts `timeout: false` to disable its built-in 300s ceiling. The DOM RequestInit +// type this project compiles against does not declare it, so widen it here rather than casting at +// each call site. +type BunRequestInit = RequestInit & { timeout?: boolean | number }; type TranscribeArgs = { file: File | Blob; @@ -52,7 +62,11 @@ export async function transcribeAudio({ detectForm.append('response_format', 'verbose_json'); detectForm.append('detect_language', 'true'); - const detectRes = await fetch(`${base}/inference`, { method: 'POST', body: detectForm }); + const detectRes = await fetch(`${base}/inference`, { + method: 'POST', + body: detectForm, + timeout: false, + } as BunRequestInit); if (!detectRes.ok) throw new Error(`Language detection failed (${detectRes.status})`); const detectJson = (await detectRes.json()) as { language?: string; @@ -93,7 +107,11 @@ export async function transcribeAudio({ transcribeForm.append('response_format', 'text'); transcribeForm.append('language', detectedLanguage); - const res = await fetch(`${base}/inference`, { method: 'POST', body: transcribeForm }); + const res = await fetch(`${base}/inference`, { + method: 'POST', + body: transcribeForm, + timeout: false, + } as BunRequestInit); if (!res.ok) throw new Error(`Transcription failed (${res.status})`); const text = (await res.text()).trim();