[BUG] Desktop + 3P gateway: Fable 5 1M selection is normalized to bare model id on save — new sessions silently fall back to the 200K row
Status Fixed / completed
Reported on v2.1.197
Maintainer reply None cached
Workaround ✓ Mentioned in description ↑
Activity 3 comments · opened Jul 2, 2026 · closed Aug 25, 2026
Environment
- Claude Desktop 1.17377.2 (bundled CLI 2.1.197), macOS 15 (Darwin 25.3)
- 3P LLM gateway config (
inferenceProvider: "gateway"), model discovery disabled - Gateway
/v1/modelsreturnsclaude-fable-5; direct requests withmodel=claude-fable-5return 200
Config (configLibrary/<uuid>.json)
{
"inferenceGatewayBaseUrl": "https://gateway.internal.example",
"modelDiscoveryEnabled": false,
"inferenceModels": [
{ "name": "claude-fable-5", "labelOverride": "Fable 5", "supports1m": true,
"anthropicFamilyTier": "fable", "isFamilyDefault": true }
]
}
Steps to reproduce
- Configure a 3P gateway with
claude-fable-5+supports1m: trueas above. The picker correctly shows two rows: "Fable 5" (200K) and "Fable 5 1M". - In the Code tab model picker, select Fable 5 1M. The active session works with the 1M window.
- Start a new session (or switch models once and come back).
Expected: new sessions use the previously selected "Fable 5 1M".
Actual: new sessions land on the bare "Fable 5" row with a 200K context budget. The user has to manually re-select "Fable 5 1M" for every session.
Root cause (from local inspection)
Two behaviors compose into the regression:
- The renderer strips
[1m]when persisting the model selection. The setModel path normalizes the id before writing__model_selector_state(per-surface sticky state incowork_account_settings.json), soclaude-fable-5[1m]is stored as bareclaude-fable-5. This matches the intentional CLI v2.1.173 change ("Fable 5 includes 1M context by default, so the suffix is now stripped automatically") — harmless on the first-party API where bare Fable 5 is 1M. - Behind a 3P gateway, the bare Fable row is budgeted at 200K. The main-process selector config generation always emits two rows for
supports1m: truemodels (bare +[1m]variant), and the bareclaude-fable-5row is not special-cased as natively-1M. On restore, the stored bare id resolves to the bare row.
So: select 1M → persisted as bare → next session resolves to the 200K row. The stored selection can never durably stay on 1M.
Verified stored state after selecting "Fable 5 1M":
"__model_selector_state": { "code": { "model": "claude-fable-5" } }
Suggested fix (any one of these)
- Treat bare
claude-fable-5(and other natively-1M models) as 1M in gateway model rows — i.e. don't emit a 200K bare row for models whose 1M window is the API default; or - Skip the
[1m]normalization when persisting selections for gateway configs where a distinct[1m]row exists; or - On restore, upgrade a stored bare id to its
[1m]variant when the bare row was created from asupports1mexpansion.
Workaround
Manually editing __model_selector_state.code.model back to claude-fable-5[1m] in cowork_account_settings.json restores the 1M default — until the next model switch re-strips it.
3 Comments
Update — confirmed repro with a controlled experiment, and the normalization is not Fable-specific.
Same environment (Desktop 1.17377.2, bundled CLI 2.1.197, 3P gateway). Steps:
claude-sonnet-5[1m]) → send a message.Stored state after step 3 (
cowork_account_settings.json):So every
[1m]selection in the Code tab picker is persisted with the suffix stripped —claude-sonnet-5[1m]→claude-sonnet-5,claude-fable-5[1m]→claude-fable-5. On restore, the bare id resolves to the 200K row generated by thesupports1m: trueconfig expansion.This means no 1M variant can ever survive as the sticky/default selection behind a 3P gateway — for any model, not just natively-1M ones like Fable 5. The impact is broader than the original title suggests.
Still reproduces on Claude Desktop 1.24012.11 (macOS 26.2, arm64; sandbox agent CLI 2.1.215) with inferenceProvider: gateway — and it is not Fable-specific. Same behaviour for claude-sonnet-5 and claude-opus-5.
The strip is still present verbatim in the shipped renderer (ion-dist/assets/v1/shared-3-*.js, sha256 f767cf96025a885df3c24e1baba643399ce3b8fbfa7ad33fc721438597b79176):
var Sh = "[1m]";
function Mh(e) { return e.endsWith("[1m]") ? e.slice(0, -4) : e }
…and it is on the write path only. The main-process resolver returns a stored pick verbatim when it is in the valid-id set, and that set contains both <id> and <id>[1m]. So a stored claude-sonnet-5[1m] would be honoured — the app just never writes one. Writing it directly into the stored selector state confirms this: the app opens on the 1M row and the session records the suffixed id, until the next picker click erases it. Fix is to persist the id verbatim at that one setModel call site.
New since this issue was filed, and worth linking: inferenceModels[].prefer1m shipped in v1.21459.0, twelve days after this report. It is defeated by exactly this bug — prefer1m is only consulted when there is no stored pick, so the first click writes a stripped bare id and prefer1m is permanently dead for that user. #80626 reports that symptom without this cause; the two are halves of one defect.
Administrators have no way around it: the base 200K row is emitted unconditionally by the row builder with no branch producing the 1M row alone, and a [1m]-suffixed name in inferenceModels is rewritten by the config schema to {name, supports1m: true} — so it enables the doubled row rather than pinning it (tried; the picker went to five rows and the session still ran the bare id).
Impact for us: a user ran ~6 weeks on 200K believing they were on 1M, with repeated Autocompact is thrashing failures. Nothing in the UI reveals which variant is live — both rows inherit the same labelOverride.
Related: #80626 (the prefer1m half), #77247 (why the bare row is 200K on a gateway at all).
Confirming this is still present on Claude 1.25927.0 (build 003700, 2026-08-04T05:34:55.000Z), bundled CLI 2.1.221 — Windows, Cowork, inferenceProvider: "gateway" (3P gateway).