From 80e8079be566bf4b470b78f56fa652dec15e7cd8 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:03:57 +0000 Subject: [PATCH] fix(ui): reject non-finite soft budget values instead of clearing Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../KeyInfoView.handleKeyUpdate.test.tsx | 21 +++++++++++++++++++ .../components/templates/key_info_view.tsx | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx b/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx index 05f025bed21..92cc9eb08a1 100644 --- a/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx +++ b/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx @@ -1,5 +1,10 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { toast } from "@/lib/toast"; + +vi.mock("@/lib/toast", () => ({ + toast: { error: vi.fn(), success: vi.fn() }, +})); // ---- Hoisted shared mocks (safe to use inside vi.mock factories) ---- const { keyUpdateCallMock, keyDeleteCallMock, mockUseAuthorized } = vi.hoisted(() => { @@ -565,4 +570,20 @@ describe("KeyInfoView handleKeyUpdate soft_budget", () => { expect(sentPayload.soft_budget).toBeNull(); expect(JSON.stringify({ ...sentPayload })).toContain('"soft_budget":null'); }); + + it("should reject an overflowing soft_budget instead of silently clearing it", async () => { + renderWithSoftBudget(25); + + fireEvent.click(screen.getByText("Settings")); + fireEvent.click(screen.getByText("Edit Settings")); + (globalThis as any).__TEST_FORM_VALUES = { + token: "tok_123", + soft_budget: "1e309", + }; + + fireEvent.click(screen.getByText("Mock Submit")); + + await waitFor(() => expect(toast.error).toHaveBeenCalled()); + expect(keyUpdateCallMock).not.toHaveBeenCalled(); + }); }); diff --git a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx index 169523f0601..b9601ded10c 100644 --- a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx @@ -217,6 +217,10 @@ export default function KeyInfoView({ null; const nextSoftBudget = formValues.soft_budget === "" || formValues.soft_budget == null ? null : Number(formValues.soft_budget); + if (nextSoftBudget !== null && !Number.isFinite(nextSoftBudget)) { + toast.error("Soft Budget must be a finite number"); + return; + } if (nextSoftBudget === previousSoftBudget) { delete formValues.soft_budget; } else {