diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx
index 54be24d2401..08a0ab7d02b 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen, waitFor } from "@testing-library/react";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { SearchToolTester } from "./SearchToolTester";
@@ -271,7 +271,15 @@ describe("SearchToolTester", () => {
});
it("should handle search errors and show notification", async () => {
- const error = new Error("Search failed");
+ const error = new Error(
+ JSON.stringify({
+ error: {
+ message: "User not allowed to access search tool: exa-search-2",
+ type: "user_search_tool_access_denied",
+ code: "403",
+ },
+ }),
+ );
vi.mocked(networking.searchToolQueryCall).mockRejectedValue(error);
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const user = userEvent.setup();
@@ -281,11 +289,29 @@ describe("SearchToolTester", () => {
const searchButton = screen.getByRole("button", { name: /search/i });
await user.click(searchButton);
await waitFor(() => {
- expect(toast.fromError).toHaveBeenCalledWith("Failed to query search tool");
+ expect(toast.fromError).toHaveBeenCalledWith(error);
});
consoleSpy.mockRestore();
});
+ it("should not show empty results or add history when search fails", async () => {
+ vi.mocked(networking.searchToolQueryCall).mockRejectedValue(new Error("Search failed"));
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
+ const user = userEvent.setup();
+ render();
+ const input = screen.getByPlaceholderText("Enter your search query...");
+ fireEvent.change(input, { target: { value: "test query" } });
+ await user.click(screen.getByRole("button", { name: /search/i }));
+
+ await waitFor(() => {
+ expect(toast.fromError).toHaveBeenCalled();
+ });
+ expect(screen.queryByText("No results found")).not.toBeInTheDocument();
+ expect(screen.queryByText("test query")).not.toBeInTheDocument();
+ expect(screen.getByText("Test your search tool")).toBeInTheDocument();
+ consoleSpy.mockRestore();
+ });
+
it("should maintain search history", async () => {
const user = userEvent.setup();
render();
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx
index 7dcef3fc6ba..646120443c7 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx
@@ -60,7 +60,7 @@ export const SearchToolTester: React.FC = ({ searchToolNa
setSearchHistory((prev) => [historyEntry, ...prev]);
} catch (error) {
console.error("Error querying search tool:", error);
- toast.fromError("Failed to query search tool");
+ toast.fromError(error);
} finally {
setIsLoading(false);
}
diff --git a/ui/litellm-dashboard/src/components/networking.test.ts b/ui/litellm-dashboard/src/components/networking.test.ts
index e7a55870345..8b0f0389772 100644
--- a/ui/litellm-dashboard/src/components/networking.test.ts
+++ b/ui/litellm-dashboard/src/components/networking.test.ts
@@ -1178,3 +1178,29 @@ describe("getUiConfig runtime backend discovery", () => {
});
});
});
+
+describe("searchToolQueryCall", () => {
+ const originalFetch = global.fetch;
+
+ afterEach(() => {
+ global.fetch = originalFetch;
+ });
+
+ it("rejects with the backend error message for a denied search", async () => {
+ const errorMessage = "User not allowed to access search tool: exa-search-2";
+ global.fetch = vi.fn().mockResolvedValue(
+ new Response(
+ JSON.stringify({
+ error: {
+ message: errorMessage,
+ type: "user_search_tool_access_denied",
+ code: "403",
+ },
+ }),
+ { status: 403 },
+ ),
+ );
+
+ await expect(Networking.searchToolQueryCall("token", "exa-search-2", "litellm")).rejects.toThrow(errorMessage);
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx
index fd25242ec34..9aade717566 100644
--- a/ui/litellm-dashboard/src/components/networking.tsx
+++ b/ui/litellm-dashboard/src/components/networking.tsx
@@ -6860,7 +6860,7 @@ export const searchToolQueryCall = async (
if (!response.ok) {
const errorData = await response.text();
await handleError(errorData);
- return null;
+ throw new Error(errorData);
}
const data = await response.json();