2026-05-26 — ClusterCard "Provisioning failed" banner is transient again¶
Motivation¶
A user reported that the dashboard's AZURE KUBERNETES SERVICE CLUSTER
card kept rendering a "Provisioning failed — Could not access resource
group rg-elb-cluster." banner even though the cluster had been
cleanly deleted and no new attempt was running. Refreshing the
browser brought the banner back, with no way to make it go away short
of clicking Dismiss in the very browser profile that hit the failure.
Root cause: the card had two persistent hydration sources for the banner:
localStorageslotelb_last_failed_provision_v1, written byuseClusterProvisioningwheneverprovision_aksFAILED.- Server-side
GET /api/aks/recent-failed-provisions?hours=24&limit=1(JobState rows for the caller, 24 h window), queried on every mount.
Either source would re-surface the same failed-attempt row on the next
page load — and (1) was even mirrored across tabs via a storage
event listener. That contract is incompatible with "show error once,
at the time of failure".
User-facing change¶
- The "Last attempt failed" / "Provisioning failed" banner now lives
only in React state for the current session. It appears the
moment
provision_aksreports FAILURE / REVOKED, and disappears on: - browser refresh (React state is dropped), or
- clicking Dismiss on the banner.
- The card no longer auto-hydrates a banner from
localStorageor from/api/aks/recent-failed-provisionson mount. - A cleanly deleted cluster no longer leaves a sticky error behind on the dashboard.
Code diff summary¶
web/src/components/cards/ClusterCard/ClusterCard.tsx- Removed
lastFailedstate, the mount-time hydrationuseEffectthat readlocalStorage+ calledaksApi.recentFailedProvisions, the cross-tabstoragelistener, and thelastFailedIsStalecomputation + dismiss effect. - Removed the
<ProvisionErrorCard raw={lastFailed.raw} … />render block at the bottom of the card. - Dropped the now-unused imports (
aksApi,loadDismissThreshold,loadLastFailedProvision,dismissLastFailedProvision,LastFailedProvision,useEffect). web/src/components/cards/ClusterCard/useClusterProvisioning.ts- Removed the
saveLastFailedProvision({...})call inside the task poll's FAILURE branch. - Removed the
dismissLastFailedProvision(Date.now())call inside the "cluster Succeeded" branch (no longer needed). - Dropped the now-unused imports.
No API surface changes. No Bicep / IaC changes. The backend route
/api/aks/recent-failed-provisions and the lastFailedProvision.ts
localStorage helper are left in place (still covered by their own
unit tests); they are simply no longer wired into the card. Future
work that genuinely wants a sticky / cross-session error feed can
re-use them.
Validation¶
cd web && npm run build→ ✓ built in 7.06 s, no TS errors.- Manual UI logic trace: the banner is now driven solely by
prov.provError, which is set inuseClusterProvisioning's task poller and cleared byprov.resetError(Dismiss / Retry) or by a page refresh. No mount-time fetch can re-introduce it.