From 225120f5d3e5ecc13fe1e0f45c8da472cf303f80 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 1 Aug 2026 12:47:36 -0500 Subject: [PATCH 1/2] fix(ui): stop provider delete from surfacing a false error toast Deleting a provider races the providers store update (via the providers-changed IPC event) against ProviderSheet's own post-delete code, so provider.name could read as undefined right after the delete succeeded, throwing and getting reported as a failure. Snapshot the name before the delete call and guard the reactive prop reads that can transiently see an undefined provider. --- .../lib/components/provider/ProviderSheet.svelte | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte b/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte index 5ae33b6dae..ff8109e742 100644 --- a/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte +++ b/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte @@ -93,7 +93,9 @@ let hasUnfilledRequired = $derived.by(() => { return requiredOptions.some(([key]) => !optionValues[key]?.trim()) }) -let versionEntry = $derived($providerVersions.byProvider[provider.name]) +let versionEntry = $derived( + provider ? $providerVersions.byProvider[provider.name] : undefined, +) let groupedOptions = $derived.by(() => { const groups: Record = {} @@ -137,7 +139,7 @@ async function loadOptions() { } $effect(() => { - if (!open) { + if (!open || !provider) { loadedFor = null return } @@ -253,10 +255,11 @@ async function handleInitialize() { } async function handleDelete() { + const name = provider.name deleting = true try { - await providerDelete(provider.name) - toasts.success(`Deleted ${provider.name}`) + await providerDelete(name) + toasts.success(`Deleted ${name}`) confirmDeleteOpen = false open = false ondeleted?.() @@ -318,6 +321,7 @@ async function handleSaveOptions() { } +{#if provider} @@ -526,3 +530,4 @@ async function handleSaveOptions() { loading={switching} onconfirm={runSwitch} /> +{/if} From e386cf8d18530d5cda89f14e9c5bb3706584bafc Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 1 Aug 2026 13:08:41 -0500 Subject: [PATCH 2/2] fix(ui): snapshot provider name in remaining async provider handlers handleSetDefault, runUpdate, runSwitch, and handleInitialize read provider.name after an await, same race as the delete handler: if the provider is deleted while one of these is in flight, the reactive prop can go undefined before the post-await read, throwing and surfacing as a misleading error toast. --- .../components/provider/ProviderSheet.svelte | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte b/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte index ff8109e742..8ac8b6be26 100644 --- a/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte +++ b/desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte @@ -151,9 +151,10 @@ $effect(() => { }) async function handleSetDefault() { + const name = provider.name try { - await providerUse(provider.name) - toasts.success(`Set ${provider.name} as default provider`) + await providerUse(name) + toasts.success(`Set ${name} as default provider`) } catch (err) { toasts.error(`Failed to set default: ${extractErrorMessage(err)}`) } @@ -181,11 +182,12 @@ function handleUpdate() { } async function runUpdate() { + const name = provider.name updating = true try { - await providerUpdate(provider.name) - toasts.success(`Updated ${provider.name}`) - await loadVersionsFor(provider.name) + await providerUpdate(name) + toasts.success(`Updated ${name}`) + await loadVersionsFor(name) await refreshUpdates() } catch (err) { toasts.error(`Failed to update: ${extractErrorMessage(err)}`) @@ -196,11 +198,12 @@ async function runUpdate() { } async function runSwitch() { + const name = provider.name switching = true try { - await providerSetVersion(provider.name, targetTag) - toasts.success(`Switched ${provider.name} to ${targetTag}`) - await loadVersionsFor(provider.name) + await providerSetVersion(name, targetTag) + toasts.success(`Switched ${name} to ${targetTag}`) + await loadVersionsFor(name) await refreshUpdates() } catch (err) { toasts.error(`Failed to switch version: ${extractErrorMessage(err)}`) @@ -227,14 +230,15 @@ function extractCliError(err: unknown): CLIError | null { } async function handleInitialize() { + const name = provider.name initializing = true initError = null - markInitializing(provider.name) + markInitializing(name) try { - await providerInit(provider.name) + await providerInit(name) const updated = await providerList() providers.set(updated) - toasts.success(`Initialized ${provider.name}`) + toasts.success(`Initialized ${name}`) } catch (err) { const cliError = extractCliError(err) if (cliError) { @@ -242,15 +246,12 @@ async function handleInitialize() { } else { initError = { code: "UNKNOWN", - message: - err instanceof Error - ? err.message - : `Failed to initialize ${provider.name}.`, + message: err instanceof Error ? err.message : `Failed to initialize ${name}.`, } } } finally { initializing = false - clearInitializing(provider.name) + clearInitializing(name) } }