fix(gui): ignore stale provider model fetch results after provider switch#12151
fix(gui): ignore stale provider model fetch results after provider switch#12151shaun0927 wants to merge 4 commits intocontinuedev:mainfrom
Conversation
Dynamic provider-model fetching allows users to switch providers while a request is still in flight. The Add Model form captured the selected provider in the callback closure, so a late response from the previous provider could still replace `fetchedModelsList` after the UI had moved on. Track the latest provider in a ref and ignore outdated async results. Add a focused regression test that reproduces the race by resolving an OpenAI fetch after switching the UI to Anthropic. Constraint: Keep the fix limited to the validated GUI race and regression coverage Rejected: Cancel in-flight requests globally | more invasive than needed for the current flow Confidence: high Scope-risk: narrow Reversibility: clean Directive: If the UI later supports multiple concurrent fetches per provider, switch from provider tracking to request-token invalidation Tested: cd gui && npm test -- --run src/forms/AddModelForm.dynamicFetchRace.test.tsx Not-tested: Full gui test suite; lint; typecheck; manual VS Code / JetBrains verification
|
All contributors have signed the CLA ✍️ ✅ |
💡 Codex Reviewcontinue/core/llm/fetchModels.ts Lines 227 to 231 in 08e0a7b
continue/gui/src/forms/AddModelForm.tsx Lines 58 to 60 in 08e0a7b The stale-response guard relies on ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
I have read the CLA Document and I hereby sign the CLA |
|
recheck |
The first fix guarded late async results, but the provider ref still moved in a passive effect. Under a fast provider switch, an older in-flight request could resolve before that effect ran and still see the stale provider value. Update the ref synchronously at provider-selection time and format the regression test so Prettier passes in CI. Constraint: Keep the follow-up limited to the bot-confirmed race window and formatting failure Rejected: Expand this PR to address provider-specific listModels config gaps | unrelated to the validated GUI race and would broaden scope Confidence: high Scope-risk: narrow Reversibility: clean Directive: If provider selection can change from additional code paths later, centralize provider-ref synchronization instead of relying on one callback Tested: cd gui && npm test -- --run src/forms/AddModelForm.dynamicFetchRace.test.tsx Tested: ./node_modules/.bin/prettier --check gui/src/forms/AddModelForm.tsx gui/src/forms/AddModelForm.dynamicFetchRace.test.tsx --ignore-path .gitignore --ignore-path .prettierignore Not-tested: Full GUI test suite; upstream CI rerun after push; JetBrains/e2e workflows
|
Addressed the provider-switch timing window in I did not fold the |
💡 Codex Reviewcontinue/gui/src/forms/AddModelForm.tsx Lines 132 to 134 in fbd092c This effect only clears continue/core/llm/fetchModels.ts Line 233 in fbd092c This returns every ID from ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…del fetches Codex surfaced a real follow-up risk in the Add Model flow: after switching between two providers that both require API keys, the form could retain the previous provider's secret long enough for a fetch click to send it to the new provider. This clears the shared API key field on provider changes and adds a focused regression test that proves a keyed-to-keyed switch leaves no stale key behind for the next fetch. Constraint: Keep the follow-up limited to the credible cross-provider credential leak in the existing GUI flow Rejected: Expand this PR to broader listModels capability filtering or provider-specific fetch config forwarding | those are separate fetch-path concerns and would widen the validated race-fix scope Confidence: high Scope-risk: narrow Reversibility: clean Directive: If the form later stores provider-specific secrets simultaneously, replace this blanket reset with per-provider key scoping rather than reusing one shared apiKey field Tested: cd gui && npm test -- --run src/forms/AddModelForm.dynamicFetchRace.test.tsx Tested: ./node_modules/.bin/prettier --check gui/src/forms/AddModelForm.tsx gui/src/forms/AddModelForm.dynamicFetchRace.test.tsx --ignore-path .gitignore --ignore-path .prettierignore Not-tested: Full GUI/e2e test matrix; provider-specific dynamic fetches against live APIs
|
Addressed the remaining in-scope Codex P1 in I still left the broader |
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="gui/src/forms/AddModelForm.tsx">
<violation number="1" location="gui/src/forms/AddModelForm.tsx:132">
P2: Clearing the API key on every provider change discards valid credentials when switching between API-key providers.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
💡 Codex Reviewcontinue/gui/src/forms/AddModelForm.tsx Lines 58 to 60 in 1078373 When a model fetch is started for provider A and the user switches to provider B before it resolves, this effect only clears ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
cubic identified a real follow-up usability bug in the Add Model race fix: switching providers cleared stale results but left the previous fetch lock active, so the newly selected provider could stay blocked until the old request finished. If that earlier request hung, the new provider could be stuck indefinitely even though stale results were already ignored. This invalidates in-flight fetch generations on provider change, clears the fetch lock for the new provider, and makes `finally` unlock only the currently active fetch. The regression test now proves a pending OpenAI request no longer blocks an immediate Anthropic fetch after switching. Constraint: Keep the follow-up limited to the provider-switch fetch lock identified by cubic Rejected: Leave `isFetchingModels` tied to the old request | new provider remains blocked until stale fetch resolves Rejected: Broaden this PR to unrelated model-list filtering/fetchModels core concerns | widens scope beyond the validated GUI race path Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future async fetch guard here should invalidate both stale results and stale loading state together Tested: cd gui && npm test -- --run src/forms/AddModelForm.dynamicFetchRace.test.tsx Tested: cd gui && npx prettier --check src/forms/AddModelForm.tsx src/forms/AddModelForm.dynamicFetchRace.test.tsx --ignore-path ../.gitignore --ignore-path ../.prettierignore Not-tested: Full GUI test suite; full PR CI rerun; manual VS Code / JetBrains validation
|
Follow-up pushed in
Validation:
@cubic-dev-ai please re-run review on the latest head. |
@shaun0927 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="gui/src/forms/AddModelForm.tsx">
<violation number="1" location="gui/src/forms/AddModelForm.tsx:81">
P2: Stale fetch results can be applied again after switching away and back to the same provider because the response is guarded only by provider identity, not the request generation.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| setFetchedModelsList((prev) => | ||
| selectedProvider.provider === providerAtFetchTime ? models : prev, | ||
| ); | ||
| if (selectedProviderRef.current === providerAtFetchTime) { |
There was a problem hiding this comment.
P2: Stale fetch results can be applied again after switching away and back to the same provider because the response is guarded only by provider identity, not the request generation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At gui/src/forms/AddModelForm.tsx, line 81:
<comment>Stale fetch results can be applied again after switching away and back to the same provider because the response is guarded only by provider identity, not the request generation.</comment>
<file context>
@@ -72,13 +78,15 @@ export function AddModelForm({
- setFetchedModelsList((prev) =>
- selectedProvider.provider === providerAtFetchTime ? models : prev,
- );
+ if (selectedProviderRef.current === providerAtFetchTime) {
+ setFetchedModelsList(models);
+ }
</file context>
| if (selectedProviderRef.current === providerAtFetchTime) { | |
| if ( | |
| selectedProviderRef.current === providerAtFetchTime && | |
| fetchGenerationRef.current === fetchGeneration | |
| ) { |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9deaa89bc9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (selectedProviderRef.current === providerAtFetchTime) { | ||
| setFetchedModelsList(models); |
There was a problem hiding this comment.
Guard fetched model updates by request generation
This update only checks selectedProviderRef before applying results, so two concurrent fetches for the same provider can still race: if a user clicks refresh twice (or changes API key/API base and refetches), the older request may resolve last and overwrite the newer model list. That reintroduces stale models in the picker even though fetchGenerationRef is already tracked; the generation check should also gate setFetchedModelsList.
Useful? React with 👍 / 👎.
Fixes #12150
Summary
AddModelFormrather than introducing global request cancellationWhy this change
PR #12046 introduced dynamic provider model fetching. That flow works, but the current callback in
gui/src/forms/AddModelForm.tsxcloses overselectedProvider, so a request started for provider A can still updatefetchedModelsListafter the user has switched to provider B.That leaves the Add Model UI in an inconsistent state where stale models from the previous provider appear under the newly selected provider.
This PR fixes that by tracking the currently selected provider at resolution time and ignoring outdated async results.
Test plan
cd gui && npm test -- --run src/forms/AddModelForm.dynamicFetchRace.test.tsx