From 21ddb71bedb62ee3a5f6845c69a2d376eeb6dae9 Mon Sep 17 00:00:00 2001 From: Roland Tannous <115670425+rolandtannous@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:18:57 +0400 Subject: [PATCH] Guard model-load success path against mid-refresh cancellation (#5944) * Guard model-load success path against mid-refresh cancellation * Skip refresh state writes when cancelled during model load --- .../features/chat/hooks/use-chat-model-runtime.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts b/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts index 8771bc888a..e6892b34fd 100644 --- a/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts +++ b/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts @@ -255,7 +255,8 @@ export function useChatModelRuntime() { [], ); - const refresh = useCallback(async () => { + const refresh = useCallback(async (options?: { signal?: AbortSignal }) => { + const signal = options?.signal; setModelsError(null); try { const [listRes, statusRes, lorasRes] = await Promise.all([ @@ -264,6 +265,11 @@ export function useChatModelRuntime() { listLoras(), ]); + // Cancellation can land while the requests above are in flight (e.g. the + // user cancels a load during this refresh). Bail before writing any + // backend state back into the store -- cancelLoading already cleared it. + if (signal?.aborted) return; + setModels(listRes.models.map(toChatModelSummary)); setLoras(lorasRes.loras.map(toLoraSummary)); @@ -405,6 +411,7 @@ export function useChatModelRuntime() { }); } } catch (error) { + if (signal?.aborted) return; const message = error instanceof Error ? error.message : "Failed to load models"; setModelsError(message); @@ -761,7 +768,7 @@ export function useChatModelRuntime() { store.setParams({ ...store.params, ...p }); } } - await refresh(); + await refresh({ signal: abortCtrl.signal }); } catch (error) { // Skip rollback if user cancelled -- model is already being unloaded. if (abortCtrl.signal.aborted) throw error; @@ -1056,6 +1063,8 @@ export function useChatModelRuntime() { try { await performLoad(); + // User cancelled mid-refresh; cancelLoading handles teardown. + if (abortCtrl.signal.aborted) return; if (loadToastDismissedRef.current) { toast.success(`${toastDisplayName} loaded`, { classNames: MODEL_LOADED_TOAST_CLASSNAMES,