From 6aa6a434fb7ea3a2e03000712c3e06eb6527fa67 Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 13 May 2026 15:12:06 +0100 Subject: [PATCH] =?UTF-8?q?agent:=20code-atomiser-fix=20=E2=80=94=20FirstR?= =?UTF-8?q?unPage=20unlisten=20on=20all=20exits=20(Race-9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move both Tauri listen() calls inside the try block with let-declared unlisten handles. A throw on the second listen() previously leaked the first subscription and the finally block could itself throw on an undefined unlistenParakeet, masking the original error. Finally now guards each handle before invoking it. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/pages/FirstRunPage.svelte | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/lib/pages/FirstRunPage.svelte b/src/lib/pages/FirstRunPage.svelte index 28cfaf6..ae4955f 100644 --- a/src/lib/pages/FirstRunPage.svelte +++ b/src/lib/pages/FirstRunPage.svelte @@ -43,15 +43,23 @@ downloadProgress = 0; downloadStartedAt = Date.now(); - const unlisten = await listen("model-download-progress", (event) => { - downloadProgress = event.payload.percent; - }); - - const unlistenParakeet = await listen("parakeet-download-progress", (event) => { - downloadProgress = event.payload.percent; - }); + // Tauri unlisten handles must be released on every exit path — + // declared outside the try so the finally can always call them, + // but assigned inside so a throw on the second listen() doesn't + // leak the first one. SettingsPage.svelte:864-880 is the + // long-form reference pattern (onMount + onDestroy). + let unlisten: (() => void) | undefined; + let unlistenParakeet: (() => void) | undefined; try { + unlisten = await listen("model-download-progress", (event) => { + downloadProgress = event.payload.percent; + }); + + unlistenParakeet = await listen("parakeet-download-progress", (event) => { + downloadProgress = event.payload.percent; + }); + if (modelId.startsWith("whisper-")) { // backend's whisper_model_id accepts the full model id via its // `other => ModelId::new(other)` fallback, so pass the id through @@ -90,8 +98,11 @@ error = `Download failed: ${e}`; downloading = false; } finally { - unlisten(); - unlistenParakeet(); + // Guard against listen() throwing before assigning the unlisten + // handle — without these checks the finally itself would throw + // and mask the original error. + if (unlisten) unlisten(); + if (unlistenParakeet) unlistenParakeet(); } }