From 3e99929d03cef9015fb596912ebd675f3b2c0be4 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 20 Feb 2026 12:01:47 +0000 Subject: [PATCH] Fix bug 1: Only fetch Anthropic models when API key exists getAnthropicModels() was called unconditionally on mount, causing a console error when no API key was set. Now chains the call after getAnthropicApiKeyExists() confirms a key is present. Includes regression test added before the fix per bug workflow. Co-Authored-By: Claude Opus 4.6 --- ...ug-1-anthropic-models-fetch-without-key.md | 24 +++++++++++++++++ frontend/src/components/Chat.test.tsx | 13 +++++++++ frontend/src/components/Chat.tsx | 27 ++++++++----------- 3 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 .story_kit/bugs/archive/bug-1-anthropic-models-fetch-without-key.md diff --git a/.story_kit/bugs/archive/bug-1-anthropic-models-fetch-without-key.md b/.story_kit/bugs/archive/bug-1-anthropic-models-fetch-without-key.md new file mode 100644 index 0000000..0b45178 --- /dev/null +++ b/.story_kit/bugs/archive/bug-1-anthropic-models-fetch-without-key.md @@ -0,0 +1,24 @@ +--- +name: Anthropic models fetched without API key +--- + +# Bug 1: Anthropic Models Fetched Without API Key + +## Symptom + +Browser console shows `Error: Anthropic API key not found. Please set your API key.` on every page load, even when the user has no Anthropic API key and is using `claude-code-pty`. + +## Root Cause + +`Chat.tsx` unconditionally calls `api.getAnthropicModels()` on mount. The server endpoint requires an API key to call the Anthropic models list API. When no key is set, the request fails with an error logged to the console. + +## Reproduction Steps + +1. Start the server without setting an Anthropic API key +2. Open the web UI +3. Open browser developer console +4. Observe the error on page load + +## Proposed Fix + +Only call `getAnthropicModels()` after `getAnthropicApiKeyExists()` confirms a key is set. Chain the calls so the models fetch is conditional. diff --git a/frontend/src/components/Chat.test.tsx b/frontend/src/components/Chat.test.tsx index 523ce0a..5144678 100644 --- a/frontend/src/components/Chat.test.tsx +++ b/frontend/src/components/Chat.test.tsx @@ -592,4 +592,17 @@ describe("Chat review panel", () => { expect(await screen.findByText("Cannot read stories")).toBeInTheDocument(); }); + + it("does not fetch Anthropic models when no API key exists", async () => { + mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false); + mockedApi.getAnthropicModels.mockClear(); + + render(); + + await waitFor(() => { + expect(mockedApi.getAnthropicApiKeyExists).toHaveBeenCalled(); + }); + + expect(mockedApi.getAnthropicModels).not.toHaveBeenCalled(); + }); }); diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 220b202..ee73eed 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -174,26 +174,21 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) { .getAnthropicApiKeyExists() .then((exists) => { setHasAnthropicKey(exists); + if (!exists) return; + return api.getAnthropicModels().then((models) => { + if (models.length > 0) { + const sortedModels = models.sort((a, b) => + a.toLowerCase().localeCompare(b.toLowerCase()), + ); + setClaudeModels(sortedModels); + } else { + setClaudeModels([]); + } + }); }) .catch((err) => { console.error(err); setHasAnthropicKey(false); - }); - - api - .getAnthropicModels() - .then((models) => { - if (models.length > 0) { - const sortedModels = models.sort((a, b) => - a.toLowerCase().localeCompare(b.toLowerCase()), - ); - setClaudeModels(sortedModels); - } else { - setClaudeModels([]); - } - }) - .catch((err) => { - console.error(err); setClaudeModels([]); }); }, []);