Changing the default model's effort in Settings can replace the saved model#16444

openedOct 6, 2026byYyaacovcorcos

Observed

In Settings, the Model row for new threads shows a different model than the saved default when the saved default's provider is turned off, unavailable, or not present on the displayed environment. Choosing an effort in that row then saves the displayed model, so the saved default is lost. At "All environments" scope the replacement is written to every connected environment.

Reproduction

  1. With Codex and Claude both enabled, set the default model in Settings to a Claude model.
  2. Turn Claude off in Settings > Providers (or view an environment where Claude is not available).
  3. Back on the Model row, it shows a Codex model.
  4. Change the effort.
  5. The saved default is now that Codex model, and stays so after Claude is turned back on.

Expected

The row keeps showing the saved default while its provider is unusable and does not let an effort change replace it. Choosing a different model on purpose still works.

Environment

macOS, T3 Code main at 9bd1d8009a. Found from code reading and a unit-level reproduction of the row's logic; the steps above follow that code path.

Proposed direction

Show the saved default with a short note that its provider is off or unavailable, and hide its effort and other options until the provider is back.

A fix along these lines is ready on a branch. I'll open it as a PR, with before/after screenshots, if this direction is agreed.

Found while porting fixes from a downstream fork (Scient). Investigated with Claude Opus 5.5 in Claude Code.

Note

Grok responding on behalf of Julius.

Triage

Thanks @yaacovcorcos for the clear write-up and repro steps, they made this straightforward to verify.

What I found

  • This reproduces from the code on main (9bd1d8009a). The Settings "Model" row for new threads doesn't render the saved defaultModelSelection directly; it renders resolveDefaultProviderModelSelection (apps/web/src/components/settings/ProjectDefaultsSettings.tsx). That keeps the stored selection only while its instance is enabled and available. Otherwise it substitutes another ready instance and its default model (apps/web/src/providerInstances.ts).
  • The effort control then saves that substitute:
    tsx
    1onModelOptionsChange={(options) =>
    2 setModel(createModelSelection(selection.instanceId, selection.model, options))
    3}
    setModel writes defaultModelSelection, and at All environments scope planScopedSettingsPatch applies it to every connected environment. The "unavailable on a selected environment" toast doesn't catch this, because the saved fallback model is available.
  • Opening the row doesn't rewrite the setting, and new threads apply the stored selection directly. The saved default only changes when an effort or other trait edit commits the substituted model. Intentional model changes go through onInstanceModelChange, a separate write.
  • The unit test "replaces an unavailable stored instance deterministically" currently locks in the substitution, and this function is only used by this row.
  • The text-generation model row has a similar shape (resolveAppModelSelectionState in SettingsPanels.tsx, with the trait picker writing the resolved selection).

Likely fix area

The direction in your report, scoped to this row, would be one option:

  • Keep showing the stored model when its provider is off, unavailable, or absent on the displayed environment, with a short note.
  • Hide effort and other trait controls for that unavailable selection, so a trait change can't persist a different model.
  • Leave intentional model changes as they are, including the existing check that refuses a model some selected environment can't use.

The text-generation row could be handled alongside this or as a follow-up. Before/after screenshots (provider off, effort change, provider back on) would be helpful for whichever direction is chosen. A maintainer will decide on the fix direction.

Reproduced in the real web app on Linux at a12d6ed942, using an isolated dev database. Saved Claude Opus 5.5, disabled Claude, changed the displayed Codex effort from Medium to High, then re-enabled Claude. The default remained GPT-6-Astra High. Without the effort change, re-enabling Claude restored the Claude default. Recording.

Reproduced again in the real web app on Linux at ed9b7ca9: saved Claude Opus 5.5, disabled Claude, changed displayed Codex effort to High, then re-enabled Claude. The persisted default remained Codex GPT-6-Astra High. Recording.

Existing fix #22193 is linked and labelled via-triage; no duplicate PR was opened. Its focused tests pass and the ordinary disabled-provider flow works in a local integration with current main. A hidden saved model still displays the wrong label while its provider is off; reproduction and remaining verification gaps are recorded on the PR. The issue remains open.