diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx index 781654ac2f8..e9def76f9cd 100644 --- a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx @@ -147,8 +147,9 @@ describe("LicenseExpiryBanner", () => { }); it("should handle expiration_date with a full ISO timestamp (not just date-only)", async () => { - // Backend might return "2026-03-22T15:00:00Z" instead of "2026-03-22" - // The component should strip the time part and still render correctly. + // Backend might return "2026-03-22T15:00:00Z" instead of "2026-03-22". + // The component strips the time part ("2026-03-22") and appends "T23:59:59Z", + // so the result is the same 8-day countdown as the date-only input. mockGetLicenseInfo.mockResolvedValue(makeLicense("2026-03-22T15:00:00Z")); render(); @@ -173,14 +174,17 @@ describe("LicenseExpiryBanner", () => { }); }); - it("should show a warning when getLicenseInfo API call fails", async () => { + it("should not show error banner on first fetch failure for non-enterprise users", async () => { mockGetLicenseInfo.mockRejectedValue(new Error("Network error")); - render(); - - expect( - await screen.findByText("Unable to verify enterprise license") - ).toBeInTheDocument(); + const { container } = render(); + await waitFor(() => { + expect(mockGetLicenseInfo).toHaveBeenCalled(); + }); + // Non-enterprise users (no prior license info) should not see the error banner + await waitFor(() => { + expect(screen.queryByRole("alert")).toBeNull(); + }); }); it("should render nothing when expiration_date is unparseable", async () => { diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx index 615be1ff979..d802ed56ad5 100644 --- a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx @@ -18,8 +18,11 @@ export const LicenseExpiryBanner: React.FC = () => { if (!accessToken) return; let cancelled = false; + let inFlight = false; const fetchLicense = () => { + if (inFlight) return; // skip if a previous request is still pending + inFlight = true; getLicenseInfo(accessToken) .then((info) => { if (!cancelled) { @@ -29,6 +32,9 @@ export const LicenseExpiryBanner: React.FC = () => { }) .catch(() => { if (!cancelled) setFetchError(true); + }) + .finally(() => { + inFlight = false; }); }; @@ -41,7 +47,10 @@ export const LicenseExpiryBanner: React.FC = () => { }; }, [accessToken]); - if (fetchError) { + // Only show a fetch-error banner if we previously confirmed this is an + // enterprise license holder. This prevents non-enterprise users from seeing + // a misleading "Unable to verify enterprise license" warning on network errors. + if (fetchError && licenseInfo?.has_license) { return (