From 04f2cad5ab38b70be99545738fd085788e33d2af Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Fri, 10 Jul 2026 17:49:28 +0000 Subject: [PATCH] Restore the Reapply target when a model load fails to start A load request that is rejected up front (validation error, gated repo, training guard) leaves the previously loaded model resident, but both the Images and Video pages had already pointed lastLoad at the failed pick, so Reapply and the resident-default seeding retried the wrong model. Snapshot the prior target before the optimistic assignment and restore it (plus the video page's canReapply flag) when the start POST rejects; successful loads and failures after the background load starts behave as before. --- studio/frontend/src/features/images/images-page.tsx | 5 +++++ studio/frontend/src/features/video/video-page.tsx | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/studio/frontend/src/features/images/images-page.tsx b/studio/frontend/src/features/images/images-page.tsx index 9425835217..8f9cdf3310 100644 --- a/studio/frontend/src/features/images/images-page.tsx +++ b/studio/frontend/src/features/images/images-page.tsx @@ -1536,6 +1536,10 @@ export function ImagesPage({ active = true }: { active?: boolean }) { lastLoadSig.current = null; loadToastId.current = toast(null, loadToastArgs(IDLE_PROGRESS)); // Remember what was loaded so "Reapply" can reload it with new advanced options. + // Snapshot the prior target first: a load that fails to START (validation, gated + // repo, training guard) leaves the previous model resident, so Reapply and the + // resident-default seeding must keep pointing at it, not at the failed pick. + const prevLastLoad = lastLoad.current; lastLoad.current = { repoId, kind: opts.kind, filename: opts.filename }; try { // Returns immediately — the load runs in the background; we poll for it. @@ -1557,6 +1561,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) { transformer_cache: transformerCache === "auto" ? undefined : transformerCache, }); } catch (err) { + lastLoad.current = prevLastLoad; dismissLoadToast(); toast.error(err instanceof Error ? err.message : "Failed to start load"); setBusy(null); diff --git a/studio/frontend/src/features/video/video-page.tsx b/studio/frontend/src/features/video/video-page.tsx index 36569aefef..49b0b7a987 100644 --- a/studio/frontend/src/features/video/video-page.tsx +++ b/studio/frontend/src/features/video/video-page.tsx @@ -936,6 +936,11 @@ export function VideoPage({ active = true }: { active?: boolean }) { dismissLoadToast(); lastLoadSig.current = null; loadToastId.current = toast(null, loadToastArgs(IDLE_PROGRESS)); + // Snapshot the prior Reapply target first: a load that fails to START (validation, + // gated repo, training guard) leaves the previous model resident, so Reapply must + // keep pointing at it, not at the failed pick. + const prevLastLoad = lastLoad.current; + const prevCanReapply = canReapply; lastLoad.current = { repoId, kind: opts.kind, filename: opts.filename }; setCanReapply(true); try { @@ -954,6 +959,8 @@ export function VideoPage({ active = true }: { active?: boolean }) { transformer_quant: transformerQuant === "auto" ? undefined : transformerQuant, }); } catch (err) { + lastLoad.current = prevLastLoad; + setCanReapply(prevCanReapply); dismissLoadToast(); toast.error(err instanceof Error ? err.message : "Failed to start load"); setBusy(null); @@ -967,6 +974,7 @@ export function VideoPage({ active = true }: { active?: boolean }) { pollLoadProgress, refreshStatus, dismissLoadToast, + canReapply, memoryMode, speedMode, attentionBackend,