From c35495ae5cbd3e27ca47062a477d1c135cdab4c4 Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Tue, 8 Sep 2026 08:34:20 -0700 Subject: [PATCH] feat: await the trigger card's recovery refetch inside the same promise chain as its saves, so a refused entry-delay commit's invalidation cannot resolve after the next commit's optimistic write and put the stale delay back on the card --- .../components/app/campaigns/sequences/CampaignFlow.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/web/src/components/app/campaigns/sequences/CampaignFlow.tsx b/web/src/components/app/campaigns/sequences/CampaignFlow.tsx index c73553a4..0ce073f1 100644 --- a/web/src/components/app/campaigns/sequences/CampaignFlow.tsx +++ b/web/src/components/app/campaigns/sequences/CampaignFlow.tsx @@ -461,11 +461,14 @@ function TriggerNode({ data }: NodeProps) { inFlight.current = inFlight.current .catch(() => {}) .then(() => updateCampaign.mutateAsync({ entry_delay_minutes: next })) - .catch((err) => { + .catch(async (err) => { toast.error(buildError(err as AppError)); // Put the optimistic value back where the server left it, so a - // refused save does not leave the card claiming a delay it never got. - void qc.invalidateQueries({ queryKey: ["campaigns", campaignId] }); + // refused save does not leave the card claiming a delay it never + // got. Awaited so the recovery refetch stays INSIDE the chain: a + // detached one could land after the next commit's optimistic + // write and put the stale value back on the card. + await qc.invalidateQueries({ queryKey: ["campaigns", campaignId] }); }); };