diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.tsx index 86cd345adb8..b82f19b416d 100644 --- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.tsx +++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.tsx @@ -151,9 +151,17 @@ const Fallbacks: React.FC = ({ accessToken, userRole, userID }) } setRouterSettings(router_settings); }); + let cancelled = false; getRouterSettingsCall(accessToken) - .then((data) => setSourcesState({ sessionKey, sources: data.source ?? {}, failed: false })) - .catch(() => setSourcesState({ sessionKey, sources: null, failed: true })); + .then((data) => { + if (!cancelled) setSourcesState({ sessionKey, sources: data.source ?? {}, failed: false }); + }) + .catch(() => { + if (!cancelled) setSourcesState({ sessionKey, sources: null, failed: true }); + }); + return () => { + cancelled = true; + }; }, [accessToken, userRole, userID, sessionKey]); const handleDeleteClick = (fallbackEntry: FallbackEntry) => { diff --git a/ui/litellm-dashboard/src/components/router_settings/index.test.tsx b/ui/litellm-dashboard/src/components/router_settings/index.test.tsx index 01f459d68e9..47ff3c2d51c 100644 --- a/ui/litellm-dashboard/src/components/router_settings/index.test.tsx +++ b/ui/litellm-dashboard/src/components/router_settings/index.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { fireEvent, renderWithProviders, screen, waitFor } from "../../../tests/test-utils"; +import { act, fireEvent, renderWithProviders, screen, waitFor } from "../../../tests/test-utils"; import userEvent from "@testing-library/user-event"; import RouterSettings from "./index"; @@ -208,10 +208,24 @@ describe("RouterSettings", () => { const { rerender } = renderWithProviders(); expect(await screen.findByRole("button", { name: /save changes/i })).toBeInTheDocument(); - vi.mocked(getRouterSettingsCall).mockReturnValue(new Promise(() => {})); - rerender(); - + let resolveStale: ( + value: typeof mockRouterSettingsResponse & { source: Record }, + ) => void = () => {}; + vi.mocked(getRouterSettingsCall).mockReturnValueOnce( + new Promise((resolve) => { + resolveStale = resolve; + }), + ); + rerender(); expect(screen.queryByRole("button", { name: /save changes/i })).not.toBeInTheDocument(); + + vi.mocked(getRouterSettingsCall).mockResolvedValueOnce({ ...mockRouterSettingsResponse, source: {} }); + rerender(); + expect(await screen.findByRole("button", { name: /save changes/i })).toBeInTheDocument(); + + resolveStale({ ...mockRouterSettingsResponse, source: {} }); + await act(async () => {}); + expect(screen.getByRole("button", { name: /save changes/i })).toBeInTheDocument(); }); it("shows an error instead of a blank page when the source request fails", async () => { diff --git a/ui/litellm-dashboard/src/components/router_settings/index.tsx b/ui/litellm-dashboard/src/components/router_settings/index.tsx index c611a7eed23..9ed4bc8c747 100644 --- a/ui/litellm-dashboard/src/components/router_settings/index.tsx +++ b/ui/litellm-dashboard/src/components/router_settings/index.tsx @@ -48,8 +48,10 @@ const RouterSettings: React.FC = ({ accessToken, userRole, selectedStrategy: initialStrategy, })); }); + let cancelled = false; getRouterSettingsCall(accessToken) .then((data) => { + if (cancelled) return; setSourcesState({ sessionKey, sources: data.source ?? {}, failed: false }); if (data.fields) { // Build metadata map for easy lookup @@ -85,7 +87,12 @@ const RouterSettings: React.FC = ({ accessToken, userRole, } } }) - .catch(() => setSourcesState({ sessionKey, sources: null, failed: true })); + .catch(() => { + if (!cancelled) setSourcesState({ sessionKey, sources: null, failed: true }); + }); + return () => { + cancelled = true; + }; }, [accessToken, userRole, userID, sessionKey]); const handleSaveChanges = async () => {