Fable 5 silently substituted in VS Code extension while the same account runs Fable 5 in the desktop app

Status Open
Reported on v2.1.221
Maintainer reply None cached
Activity 0 comments · opened Aug 5, 2026

Summary

On the same machine, same account, same subscription (Max 5x, no API key anywhere — subscription auth only), Fable 5 works in the Claude desktop app but is silently swapped for Sonnet 5 in the VS Code extension. The extension's model picker reports Fable as active before sending, then switches after the first message. There is no API error — the substitution is entirely client-side.

Steps to reproduce

  1. In the VS Code extension, select Fable 5. /model confirms:

``
Current model: Fable 5 (session override from plan mode)
Base model: Opus 5 (effort: xhigh)
``

  1. Send any message.
  2. Get: Switched to Sonnet 5 for this session · Fable 5 requires usage credits · /model to change

Expected vs actual

Expected: either Fable runs, or the UI tells me up front that it can't — before I've written a prompt and hit send.

Actual: the UI reports Fable as the active model, then silently downgrades on send. Without reading the system notice you can work for a long time convinced you're on Fable while you're on Sonnet.

Three separate problems

1. Inconsistent entitlement across first-party clients

Same account, same moment in time:

| Client | Fable 5 |
|---|---|
| Claude desktop app (spawns claude-code 2.1.221 with --model claude-fable-5) | works |
| Extension binary 2.1.222, headless -p / stream-json | works |
| VS Code extension 2.1.222, interactive | blocked |
| claude.ai in the browser | works |

If the account genuinely lacks entitlement, the desktop app should not be serving Fable either. If it does have entitlement, the extension should not be blocking.

2. Consenting to the dialog does not work

With the model set at spawn time, the extension shows the real consent dialog ("Fable 5 requires usage credits … Buy usage credits on claude.ai / Switch to the default model and continue") instead of substituting silently. But pressing consent still ends in a fallback — the transcript records model_consent_fallback with choice: "consent", and the telemetry outcome is overage_enable_deferred. So the consent path is a dead end: it offers an agreement that cannot succeed.

Related: client state reports spend.can_purchase_credits: false and spend.can_toggle: false for a personal Max org, so the "Buy usage credits" button may also be a dead end for this account type.

3. Client and server disagree

The API serves the model without complaint — the same extension binary returns a normal completion from claude-fable-5 with service_tier: standard — while simultaneously returning the header:

anthropic-ratelimit-unified-overage-disabled-reason: org_level_disabled

The client stores that header value and gates on it. Notably the gate's accept-list contains org_level_disabled_until (with the _until suffix), while the server sends org_level_disabled — so a stored, previously granted consent is treated as not granted. This looks like a string mismatch between the server's value and the client's expected set, but I can only observe it from outside.

Environment

  • Extension: anthropic.claude-code-2.1.222-darwin-arm64, macOS 15 (Darwin 25.5.0), arm64
  • Desktop app: Claude 1.25927.0, bundling claude-code 2.1.221
  • Plan: Max 5x, billingType: stripe_subscription, personal org (UUID redacted), created 2026-08-03
  • No ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN set anywhere (env, shell profiles, terminal.integrated.env)

Things that do not help

Restarting VS Code, /logout + /login, clearing the cached server state in ~/.claude.json (it is re-fetched identical — the reason arrives as a response header, so it is not a cache), and claudeCode.selectedModel (which appears to be unread by extension 2.1.222 — 0 occurrences in extension.js/webview).

The smallest useful fix

Even leaving the entitlement question aside: don't show a model as active if sending will substitute it. Gate at selection time, with the reason, instead of after the prompt is written.

View original on GitHub ↗