Fable 5 falsely walled behind usage credits in interactive picker for Max accounts using a setup-token (headless -p works)

Status Open
Reported on v2.1.216
Maintainer reply None cached
Activity 16 comments · opened Jul 20, 2026

Summary

On a Max account authenticated with a claude setup-token (CLAUDE_CODE_OAUTH_TOKEN, headless/automation auth), the interactive model picker walls Fable 5 behind usage credits:

Fable 5 runs on usage credits, purchased separately from your plan. You don't have usage credits yet.

…even though the same account + same token grants Fable fine in headless (-p) mode, and claude.ai states Fable 5 is a standard part of the Max plan. The interactive picker appears to fail-closed because the setup-token's account profile doesn't include a plan tier (subscriptionType), so the client can't confirm the account is Max.

Environment

  • Claude Code 2.1.216 (latest; native installer, Linux/Debian 13)
  • Auth: claude setup-tokenCLAUDE_CODE_OAUTH_TOKEN (no credentials.json present)
  • Plan: Max (oauthAccount present, hasPrimaryApiKey: false, 0 approved API keys — subscription auth, not API billing)

Reproduction

  1. Authenticate a Max account via claude setup-token and run with CLAUDE_CODE_OAUTH_TOKEN set (no credentials.json on disk).
  2. In an interactive session, run /model and select Fable 5 — or launch with claude --model claude-fable-5.
  3. Observe the gate: "Fable 5 runs on usage credits… You don't have usage credits yet," offering only "Set up usage credits" or a fallback model (Sonnet 5 / keep current). Fable cannot be selected.

Expected

A Max account should be able to select Fable 5 interactively regardless of whether it authenticated via /login or a setup-token — consistent with headless behavior and with claude.ai's "Fable 5 is a standard part of your Max plan."

Evidence that the server does grant Fable to this account (only the interactive client walls it)

Headless request on the exact same token succeeds and is genuinely served by Fable:

echo hi | claude -p --model claude-fable-5 --output-format json | jq '{modelUsage: (.modelUsage|keys), is_error}'
{ "modelUsage": ["claude-fable-5", "claude-haiku-4-5-20251001"], "is_error": false }

No credits are consumed; is_error: false; claude-fable-5 is the serving model.

Likely root cause

Under a setup-token, ~/.claude.json oauthAccount has no subscriptionType field (only seatTier / userRateLimitTier / organizationRateLimitTier). The Fable credits gate appears to check the plan tier client-side and, unable to confirm Max, fail-closes to "credits required." Relevant cached flags:

  • tengu_saffron_credits_only_tiers: ["enterprise"] — Max is not a credits-only tier
  • tengu_saffron_lattice.planLimitsEndDate: 2026-07-20T07:00:00Z — the "included in plan limits" launch window closed, after which the client applies the credits gate

Because oauthAccount carries profileFetchedAt and is re-fetched from the server, the missing subscriptionType cannot be patched locally — the server profile for a setup-token simply omits it.

Suggested fix directions

  • Have the interactive Fable entitlement check fail-open (or resolve the tier server-side) when the local profile lacks subscriptionType but the account otherwise has access — matching what the headless path already does.
  • Or include subscriptionType in the account profile returned for setup-token auth.

Also (minor, same root cause family)

In-product /feedback submission returns HTTP 403 under the setup-token (couldn't file this in-product), which is why this is on GitHub. The setup-token appears to lack scope for the feedback endpoint as well.

View original on GitHub ↗

16 Comments

rflcrz · 1 month ago

Confirming on macOS too (Claude Code 2.1.216, Max 20x) — with an A/B that isolates
the cause to the credential profile, exactly as you suspect.

Same account, same machine, same interactive /model picker; only the credential differs:

  • CLAUDE_CODE_OAUTH_TOKEN (setup-token): Fable 5 walled — "usage credits /

request from your admin." oauthAccount never populates (/status shows no email),
so no subscriptionType.

  • Interactive /login (subscription OAuth), same Max account: header shows

"Claude Max," Fable 5 selectable and served normally.

So it isn't the interactive path itself — it's specifically the setup-token profile
lacking subscriptionType, with the entitlement check fail-closing. Matches your
root cause.

Practical note for others: an injected CLAUDE_CODE_OAUTH_TOKEN takes precedence over
a stored /login credential, so the local workaround is to unset the token in that
shell and let the /login credential (Keychain / .credentials.json) drive. On a
truly headless/token-only host that's not an option — so +1 for fail-open, or
returning subscriptionType for setup-token auth.

AndrewLuebke · 1 month ago

Confirming the workaround on Linux (Claude Code 2.1.216, Max), and adding a variant that makes it viable on an always-on / multi-instance host where "just unset the token and /login" isn't otherwise safe.

Same A/B — one account, one machine, only the credential differs:

  • Setup-token (CLAUDE_CODE_OAUTH_TOKEN): interactive /model fable"Fable 5 runs on usage credits… you don't have usage credits yet." ~/.claude.json has no subscriptionType; the setup-token's server profile never populates it. profileFetchedAt updates on each launch but the tier stays absent — so minting a fresh setup-token does not help (verified).
  • Interactive /login (subscription OAuth), same Max account: subscriptionType: "max" present, Fable selectable and served.

So the entitlement check fail-closes whenever it can't read the tier. Matches @rflcrz's root cause.

Authoritative "is it really Fable" check (a text reply can't prove which model served the turn):

echo hi | claude -p --model claude-fable-5 --output-format json | jq '.modelUsage | keys'
# → ["claude-fable-5", ...]   with is_error:false

Why the plain workaround is awkward on a multi-instance host: unsetting CLAUDE_CODE_OAUTH_TOKEN and using a /login credential works — but if you /login into the default config dir on a box running several concurrent Claude Code processes, they share one .credentials.json and rotate each other's refresh tokens → periodic forced re-auth. Avoiding exactly that is why the setup-token is in use in the first place.

Isolated-CLAUDE_CONFIG_DIR variant that sidesteps both problems — give the interactive Fable session its own config dir with its own /login credential, and unset the injected token for that session only:

claude-fable() {
  env -u CLAUDE_CODE_OAUTH_TOKEN CLAUDE_CONFIG_DIR="$HOME/.config/claude-fable" \
    claude --model claude-fable-5 "$@"
}

A one-time /login inside that dir writes a subscriptionType: "max" credential there, and the picker then offers Fable normally. Because the credential lives in a separate dir driven by a single session, it never rotates against the setup-token automation sessions (which keep using the env token in the default dir). Global CLAUDE.md / memory / settings.json can be symlinked back into the isolated dir so nothing is lost.

Verified end-to-end: isolated cred subscriptionType:"max", default config dir stays credential-free, and the --output-format json usage ledger confirms claude-fable-5 served the turn with is_error:false.

That's a working stopgap, but still a workaround. On a truly headless / token-only host you can't /login at all, so +1 to a real fix: either fail open when the tier can't be determined for an otherwise-valid subscription token, or have setup-token auth return subscriptionType.

ramazanpolat · 1 month ago

Confirming the same bug on macOS (arm64), Claude Code 2.1.216, Max 5x — with a few extra data points:

  • A freshly minted token doesn't help. Token regenerated via claude setup-token the same day; the interactive picker still gates Fable 5, so it's the auth path itself, not a stale token.
  • Second dialog variant when the account has a credit balance (usage credits intentionally turned off at org level):

> Fable 5 runs on usage credits — you have $XX in credits.
> Usage credits are turned off. Re-enable to use Fable 5.
> 1. Set up usage credits on claude.ai
> 2. No, keep my current model

  • The claude.ai Usage page banner's advice doesn't work on the token path. The banner says "Fable 5 is still included with your Max plan. If you see a prompt to set up usage credits for it, restart Claude Code." — restarting changes nothing while CLAUDE_CODE_OAUTH_TOKEN is set.
  • The account profile cache never refreshes under token auth: oauthAccount.profileFetchedAt in ~/.claude.json stayed pinned to a fetch from 5 days earlier (retaining a pre-July-19 hasExtraUsageEnabled value) across multiple sessions and even a /login, since the env var takes precedence over the fresh keychain credentials.
  • Same as OP: headless claude --model claude-fable-5 -p works fine with the token; and the same account via keychain OAuth (env -u CLAUDE_CODE_OAUTH_TOKEN claude) selects Fable 5 normally with no gate. Not a limit issue (Fable weekly usage at 5%).

Workaround: drop the global CLAUDE_CODE_OAUTH_TOKEN export and rely on keychain login; headless jobs that genuinely need the token remain affected.

rob-mur · 1 month ago

Can confirm I get exactly the same behaviour on Team edition

grigoryosifov · 1 month ago

Confirming on macOS (arm64), Claude Code 2.1.220, Max 20x, with a setup-token freshly minted 2026-07-20. Two updates on top of the thread so far:

1. The headless escape hatch is gone. The OP's exact -p check is now refused under the token:

echo hi | claude -p --model claude-fable-5 --output-format json | jq '{modelUsage: (.modelUsage|keys), is_error, result}'
{
  "modelUsage": [],
  "is_error": true,
  "result": "You're out of usage credits. Run /usage-credits to keep using Fable 5 or /model to switch models."
}

Controls, run back to back: the same token with --model claude-sonnet-5 works fine, and the same account without the token (keychain /login credential) serves Fable normally in both headless and interactive mode. The interactive gate is also still present on 2.1.220, with a detail: the session header shows "Claude API" instead of "Claude Max", launching with --model claude-fable-5 is accepted, and the "Fable 5 now uses usage credits" dialog only fires when the first message is submitted.

2. The refusal is now server-side, so no client fix can patch it. A direct POST /v1/messages probe with the OAuth token (anthropic-beta: oauth-2025-04-20) returns rate_limit_error ("This request would exceed your account's rate limit") for claude-fable-5, while the identical request with claude-sonnet-5 on the same token returns 200. So the 2.1.220 changelog entry "Fixed the Fable model row showing 'Requires usage credits' for plans that include it, when a stale cache had baked the label in" does not cover this path.

One more data point consistent with the root cause: the setup-token cannot read the account profile at all. GET /api/oauth/profile with the token returns 403, "OAuth token does not meet scope requirement any_of(user:profile, user:office)". That explains why oauthAccount never populates under token auth (no email in /status, no subscriptionType), and suggests the server-side entitlement check cannot resolve the plan tier for these tokens either.

Net state as of 2026-07-25: on a Max account without usage credits, a setup-token cannot use Fable 5 at all, headless or interactive. The only working paths are /login credentials, default or isolated CLAUDE_CONFIG_DIR. +1 for resolving the tier server-side for setup-token auth.

vzellmeister · 1 month ago
Investigated and written by Claude (Claude Code), posted by the account holder. Findings were reproduced on this machine; the code excerpts are from the locally installed 2.1.220 build.

Confirming on Linux (WSL2), Claude Code 2.1.220, Max 20x, setup-token auth. Adding a
teardown of the code path and a workaround that keeps the setup-token — the thread so far
concludes you have to drop the token and /login, which isn't viable for headless or
multi-instance setups.

Root cause: ms() short-circuits on the env token

The credential resolver returns early when CLAUDE_CODE_OAUTH_TOKEN is set and builds the
credential object entirely from environment variables.credentials.json is never
consulted on that path:

ms = Vr(() => {
  if (rf()) return null;
  if (Z.CLAUDE_CODE_OAUTH_TOKEN) return {
    accessToken:      Z.CLAUDE_CODE_OAUTH_TOKEN,
    refreshToken:     null,
    expiresAt:        null,
    scopes:           Beu(),
    subscriptionType: Z.CLAUDE_CODE_SUBSCRIPTION_TYPE || null,
    rateLimitTier:    Z.CLAUDE_CODE_RATE_LIMIT_TIER    || null
  };
  // …reads claudeAiOauth from the credentials store — unreachable while the env var is set
});

The tier the gate uses reads straight off that object:

function Ca(){ /* … */ let e = ms(); if (!e) return null; return e.subscriptionType ?? null }
function F1e(){ return Ca() === "max" }

So with a setup-token and neither env var set, Ca() is null, F1e() is false, the picker
can't confirm Max, and past tengu_saffron_lattice.planLimitsEndDate (2026-07-20T07:00:00Z) it
fail-closes to the credits gate. That matches the missing-subscriptionType diagnosis in the OP
and @rflcrz's A/B — this is the specific line that makes it missing.

Workaround: set the two env vars, keep the token

export CLAUDE_CODE_OAUTH_TOKEN=…       # unchanged
export CLAUDE_CODE_SUBSCRIPTION_TYPE=max
export CLAUDE_CODE_RATE_LIMIT_TIER=default_claude_max_20x   # or default_claude_max_5x

Fresh shell → /model → Fable selectable, no consent prompt, served normally. No /login, no
.credentials.json, no change to the automation auth path. CLAUDE_CODE_SUBSCRIPTION_TYPE is the
one that matters for the gate; CLAUDE_CODE_RATE_LIMIT_TIER only feeds things like /upgrade's
"already on the highest Max plan" response, but it's worth setting correctly.

This also explains why re-minting the token doesn't help (@AndrewLuebke, @ramazanpolat): the field
was never going to come from the token — it comes from an env var the setup-token flow doesn't
tell you to set.

Negative result, to save others the detour

Injecting subscriptionType: "max" into oauthAccount in ~/.claude.json does nothing.
That object is a different accessor (dc()) from the credential object (ms()) the gate reads —
compare itu() reading dc()?.seatTier against Ca() reading ms()?.subscriptionType. I tried
that first and it had no effect; the env var is the only lever on that code path.

Data point on 2.1.220 headless

@grigoryosifov reports the headless -p escape hatch is refused on 2.1.220. On this machine it
still worked on 2.1.220 under a plain setup-token with neither env var set — run before applying
the workaround:

{ "modelUsage": ["claude-fable-5"], "is_error": false, "result": "Hi! What can I help you with today?" }

So headless refusal doesn't look purely version-gated; may depend on account state (remaining
included Fable allowance) or platform. Both reports are on 2.1.220, differing OS.

Suggested fix

The /upgrade path already handles this correctly — when subscriptionType/rateLimitTier are
absent it falls back to the access token and asks the server:

if (s?.subscriptionType && s?.rateLimitTier) a = /* local check */;
else if (s?.accessToken) { let l = await yst(s.accessToken); a = l?.organization?.organization_type === "claude_max" && …; }

Giving the Fable entitlement check the same server fallback — or having setup-token populate
subscriptionType — would fix this without users needing to know either env var exists.

AndrewLuebke · 1 month ago

Confirming @vzellmeister's CLAUDE_CODE_SUBSCRIPTION_TYPE fix works — Max 20x, setup-token auth, Claude Code 2.1.220, Linux. Interactive /model fable now selects Fable 5 cleanly with no credits wall, and the session header flips from "Claude API" → "Claude Max" — the tell that the gate is finally in subscription mode.

For anyone on a token-only / headless / multi-instance / CI host where dropping the token and /login isn't an option: this keeps the setup-token. Add two lines wherever you export CLAUDE_CODE_OAUTH_TOKEN (shell profile, env file, CI secrets):

export CLAUDE_CODE_SUBSCRIPTION_TYPE=max
export CLAUDE_CODE_RATE_LIMIT_TIER=default_claude_max_20x   # or default_claude_max_5x — your tier

Then start a fresh session (these are read at process start, so existing sessions won't pick them up). Per @vzellmeister's teardown, the credential resolver builds subscriptionType from CLAUDE_CODE_SUBSCRIPTION_TYPE when a token is set, and the picker's subscriptionType === "max" check then passes. CLAUDE_CODE_SUBSCRIPTION_TYPE=max is the actual lever; CLAUDE_CODE_RATE_LIMIT_TIER only feeds /upgrade messaging.

Gotcha on picking your tier: don't read it from the setup-token's ~/.claude.json — under token auth that profile is stale (it can't fetch /api/oauth/profile: 403, "does not meet scope requirement user:profile"). Mine reported default_claude_max_5x there, but I'm actually 20x; the correct value came from a /login credential's rateLimitTier and from claude.ai. Since it only affects /upgrade text, a wrong guess there is harmless — SUBSCRIPTION_TYPE=max is what unblocks Fable.

This is truthful (it just declares your real tier), client-side only — no /login, no .credentials.json, no change to billing or your automation auth path; the server still authorizes against your real plan.

Still a workaround for a client bug, so +1 to the proper fix: give the Fable entitlement check the same server fallback /upgrade already uses when subscriptionType/rateLimitTier are absent, or have claude setup-token populate them.

junxie6 · 1 month ago

I have a Claude Max plan and use both the Claude Code CLI and the Claude Code extension in VS Code on a headless server with an OAuth token. Here is how I got both approaches working.

First, reset ~/.claude.json and ~/.claude/settings.json:

cat > ~/.claude.json << 'EOF'
{
  "hasCompletedOnboarding": true
}
EOF
cat > ~/.claude/settings.json << 'EOF'
{
  "model": "claude-fable-5",
  "effortLevel": "xhigh",
  "theme": "light"
}
EOF

Next, reset ~/.claude/.credentials.json:

cat > ~/.claude/.credentials.json << 'EOF'
{
  "claudeAiOauth": {
    "accessToken": "sk-ant-oat01-*****",
    "refreshToken": "",
    "expiresAt": 9999999999999,
    "scopes": [
      "user:file_upload",
      "user:inference",
      "user:mcp_servers",
      "user:profile",
      "user:sessions:claude_code"
    ],
    "subscriptionType": "max",
    "rateLimitTier": "default_claude_max_5x"
  },
  "organizationUuid": ""
}
EOF

Replace the value of the accessToken field with the token generated by:

claude setup-token

It is okay to leave the organizationUuid field empty.

Next, unset the following Claude Code-related environment variables:

unset CLAUDE_CODE_OAUTH_TOKEN
unset CLAUDE_CODE_SUBSCRIPTION_TYPE
unset CLAUDE_CODE_RATE_LIMIT_TIER

Also, make sure these variables are either removed from or commented out in ~/.bashrc.

You can check for them by running:

grep CLAUDE_CODE_ ~/.bashrc

Then completely close VS Code.

Kill the VS Code Server process. This step is important:

sudo pkill -f vscode-server

Reopen VS Code and start a new Claude Code session.

Finally, run:

/usage

You should see something similar to:

Auth method: Claude API
Plan: Claude Max
ilketekss-cmd · 1 month ago

Same issue here, adding a data point with additional evidence.

Environment

  • Claude Code 2.1.220, Windows 11
  • Claude Max 20x plan (organizationType: "claude_max", organizationRateLimitTier: "default_claude_max_20x" in .claude.json)
  • Billing type: google_play_subscription (subscribed via Google Play)
  • Extra usage: cachedExtraUsageDisabledReason: "org_level_disabled"

Symptom

  • /model picker shows Claude Fable 5 behind a "usage credits required" wall and refuses selection.
  • Meanwhile, headless mode runs Fable 5 without any issue on the same account:

claude -p "test" --model claude-fable-5 → works (verified 3 times on 2026-07-30).

  • additionalModelOptionsCache in .claude.json lists Fable as an available option:

{"value":"claude-fable-5","label":"Fable","description":"Fable 5 · Most capable for your hardest and longest-running tasks"}

  • At some point the CLI also displayed the emergency tip:

"Claude Fable 5 is currently unavailable. Please use Opus 4.8 or another available model."
(lastShownEmergencyTip in .claude.json), pointing to the fable-mythos-access page —
so the gating appears to be server-side classification, not local config.

Client-side causes ruled out

  • Removed the [1m] model suffix everywhere (ref #79337, #74562).
  • Deleted cachedGrowthBookFeatures, cachedExperimentFeatures, cachedExperimentData,

clientDataCacheSlots from .claude.json — wall persisted after full restart.

  • settings.json model pinned to plain claude-fable-5.

Impact / suspicion
Interactive picker entitlement check seems to misclassify Max accounts with
google_play_subscription billing (works fine on a second account with
stripe_subscription? — no: our stripe-billed second account shows the same wall),
while the non-interactive path grants access correctly.

Happy to provide sanitized .claude.json fields or run a debug build.

AndrewLuebke · 27 days ago

Addendum — you don't need a shell export; settings.json[env] works too (and is cleaner for containers / CI / wrappers).

The two vars don't have to be shell exports. Declaring them in ~/.claude/settings.json's "env" block has the same effect on the interactive gate, because Claude Code writes that block into its own process.env at startup — per the docs: "Claude Code reads them directly from the file, so they take effect no matter how claude was launched."

{
  "env": {
    "CLAUDE_CODE_SUBSCRIPTION_TYPE": "max",
    "CLAUDE_CODE_RATE_LIMIT_TIER": "default_claude_max_20x"
  }
}

This is the better home whenever there's no convenient shell-init to add exports to — headless containers, CI, or tools that spawn claude for you (e.g. the AgenC feature request at mieubrisse/agenc#22, which hit exactly this: a stored setup-token with no shell export to piggyback on). The OAuth token stays wherever it lives; only these two non-secret vars move into settings.

Verified on Max 20x / 2.1.220 (Linux) by declaring them only in settings.json[env] and stripping them from the shell env at launch (env -u CLAUDE_CODE_SUBSCRIPTION_TYPE -u CLAUDE_CODE_RATE_LIMIT_TIER claude): the header still shows "Fable 5 · Claude Max" (not "Claude API") and /model fable selects clean. Credit @mieubrisse for confirming the settings.json path.

mieubrisse · 27 days ago

Wow the AI age is magical.... no idea how you found that comment @AndrewLuebke , but yes I can confirm it works.

Hive minds be crazy 🚀

ripperhe · 26 days ago

@AndrewLuebke You saved my life! 😭🙏

ripperhe · 26 days ago

Data point: this also works for Claude Team accounts, not just Max.

Environment: Claude Code 2.1.215, macOS, VS Code extension, setup-token auth (CLAUDE_CODE_OAUTH_TOKEN injected by a wrapper).

My account is a Team plan (organizationType: "claude_team", seat tier team_tier_1, userRateLimitTier: "default_claude_max_5x"), so CLAUDE_CODE_SUBSCRIPTION_TYPE=max isn't literally truthful for me — but since the picker's check only accepts the literal "max", I set it anyway:

CLAUDE_CODE_SUBSCRIPTION_TYPE=max
CLAUDE_CODE_RATE_LIMIT_TIER=default_claude_max_5x

Result: interactive /model fable selects cleanly, no credits wall.

A/B verified: removing only CLAUDE_CODE_SUBSCRIPTION_TYPE (same token, fresh session) brings the "Fable 5 requires usage credits" wall right back; adding it back unlocks again. So the gate reads this env var directly with no cached state involved.

The server side is fine with it — this Team seat does have Fable plan access (same account via /login works and gets the dedicated Fable weekly window), so the env var is just papering over the client's blind spot for non-max subscription types. +1 to the proper fix: give the Fable entitlement check the same server fallback /upgrade uses.

AndrewLuebke · 25 days ago

Data point: 2.1.222 does NOT fix the setup-token subset — the workaround is still required here. Every "fixed in 2.1.222" report so far is /login/subscription auth; this fills the gap for CLAUDE_CODE_OAUTH_TOKEN / claude setup-token auth.

Environment: Claude Code 2.1.222 (Linux, Max 20x), setup-token auth, isolated CLAUDE_CONFIG_DIR throughout so nothing touched my normal config.

The interactive wall still fires without the workaround. With subscriptionType genuinely absent (no CLAUDE_CODE_SUBSCRIPTION_TYPE), a fresh 2.1.222 session on the setup-token still hits the consent wall on the first message:

Fable 5 now uses usage credits… You don't have usage credits yet. 1. Set up usage credits 2. Switch to Opus 5 and continue

Adding CLAUDE_CODE_SUBSCRIPTION_TYPE=max (shell export or settings.json[env]) clears it, exactly as before 2.1.222. The fix @wenqifu verified addressed a present-but-misread entitlement on the /login path; the setup-token case has subscriptionType genuinely absent (the user:profile 403 on /api/oauth/profile), so there's nothing for that fix to read correctly — different root cause, untouched by 2.1.222.

Why the workaround works — it changes the server's flag evaluation, not just a client-side check. Sampling cachedGrowthBookFeatures.tengu_saffron_lattice across fresh fetches on the same account, minutes apart, the result tracked the subscriptionType attribute deterministically:

| CLAUDE_CODE_SUBSCRIPTION_TYPE | tengu_saffron_lattice | n |
|---|---|---|
| unset (setup-token's natural state) | {enabled: true, planLimitsEndDate: "2026-07-20"} → promo-over | 7/7 |
| max | {enabled: false} → no gate | 3/3 |

Claude Code sends subscriptionType as a GrowthBook attribute; with max the server evaluates the promo as not-applicable, without it the promo-over payload comes back and the client walls. On this account the split was clean with no crossover — which suggests the "flaky, same-attributes" behavior others reported (@FZ1010) is at least partly attribute-driven rather than pure server noise, though I can't rule out additional noise on other accounts.

One 2.1.222-specific gotcha: the session header read "Claude Max" and it still walled on the first message. So in 2.1.222 the header/mode indicator and the Fable consent gate are computed from different inputs — the header is no longer a reliable "am I walled" tell under token auth; the first-message consent prompt is.

Net: setup-token users on 2.1.222 still need CLAUDE_CODE_SUBSCRIPTION_TYPE=max (shell export or settings.json[env]). The proper fix is still owed server-side: give the Fable entitlement check / GrowthBook eval a path that works for inference-only-scope tokens, so subscriptionType resolves without user:profile.

Repro (isolated, non-destructive; CLAUDE_CODE_OAUTH_TOKEN set in the shell):

D=$(mktemp -d); printf '{"hasCompletedOnboarding":true}\n' > "$D/.claude.json"
env -u CLAUDE_CODE_SUBSCRIPTION_TYPE CLAUDE_CONFIG_DIR="$D" claude --model claude-fable-5
# → walls on first message. Re-run with CLAUDE_CODE_SUBSCRIPTION_TYPE=max → selects clean.
jatingartan · 23 days ago

Same issue here. Windows 11, VS Code extension 2.1.224, personal Max 20x, setup-token auth.

Heads up for anyone on a recent build: the CLAUDE_CODE_SUBSCRIPTION_TYPE / CLAUDE_CODE_RATE_LIMIT_TIER fix does work, but it isn't enough on its own anymore. It got me the "Claude Max" header and Opus 1M context back, but Fable still wasn't selectable — it just disappeared from /model entirely instead of showing the credits warning. So if Fable vanished from your list after applying the workaround, nothing is broken on your end and your access wasn't revoked.

The model itself is fine. This works on the same account and token:

claude -p --model claude-fable-5 "hi"
It's only the picker hiding it.

What got it back in the list — add these next to the other two in the env block of ~/.claude/settings.json:

"ANTHROPIC_CUSTOM_MODEL_OPTION": "claude-fable-5",
"ANTHROPIC_CUSTOM_MODEL_OPTION_NAME": "Fable",
"ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION": "Fable 5"
Restart after saving. Fable shows up in /model and works normally. This env var appends a row to the picker without running any of the entitlement checks, which is why it sidesteps the bug.

One note for the maintainers: my ~/.claude.json has cachedExtraUsageDisabledReason: "org_level_disabled" on a personal account with no organization. It reads like an admin policy block and cost me a lot of time before I realised it was just a stale value from the profile fetch failing.

smm-h · 22 days ago

Shameless plug, but i just released claudewheel v0.24.1 where this exact bug -- which was annoying me -- is fixed: https://github.com/smm-h/claudewheel/releases/tag/v0.24.1

works with oauth too