[BUG] CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK=1 arms the silent refusal-fallback path instead of disabling it (2.1.220)

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

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Version: 2.1.220, linux-x64

Also filed via /bug: feedback ID eafd0990-7ac8-4545-8391-30fb6dddcae0 (that report has the
full session transcript with the raw dumps)
Related, but not a duplicate

Existing issues report the symptoms of silent refusal fallback. None identifies the mechanism, and
none mentions CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK .

This issue is about that env var not working:

#82319 — 17 documented silent Fable 5 → Opus reroutes; notification promise not honored
#67246 — notice shown, but /model cannot override (open since 2026-06-10, 11 comments)
#76660 — silent degradation in desktop app; /model switch-back reverts next turn
#67469 — same complaint, auto-closed as duplicate of #67246 by bot after 3 days
#34281 — fallback notification behavior undocumented

Anthropic's published page states: "Users will be notified if a
request to Fable 5 is blocked, and the request will instead be sent to Opus 4.8."

The routing policy is therefore official and not in dispute here.

What follows is a defect in the documented opt-out for it.

Summary

There are two refusal-fallback lanes. The documented env var gates the visible one correctly, but
the silent one is reached only when no visible fallback is armed, setting the flag is the
precondition that arms the path it is supposed to block.

Evidence

Offsets are byte offsets into ~/.local/share/claude/versions/2.1.220.
The gate:

function nV(){ return !Z.CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK && !VCe() }

Visible lane honours it (tld):

let t = nV(), ...
o = !e.alreadyUsed && !e.declined && t && !r ? n : void 0;   // visibleModel

Consumer gate uses ||, so a silent arm satisfies it alone:

let Je = mr.routeMatched==null && (Jl!==void 0 || mr.silentArmAtTrigger===!0);
if(!(Je?b:!St&&Pks(T,qWi())) && !C && (nV()||Je)){ ... }

The rearm never consults nV():

function ZWi(e){ if(mit(e)) return; return hit() && Wu(e)===Wu(EE()) ? e : void 0 }

...and is reached only when the visible lane is unarmed:

let Ao = Yr==="refusal" && Fr===void 0 ? i.refusalFallbackSilentRearm?.() : void 0;

Chain

flag setnV() false → visibleModel undefined → Fr undefined → Fr===void 0 is exactly
ZWi's precondition → silentArmAtTrigger:trueJe true → (nV()||Je) passes.

The defect is only reachable with the flag on, which is plausibly why it survived review.

Secondary, not a bug claim, a design question

The silent lane's continuation prompt instructs the substitute model:

"...do not apologize or recap, and do not mention the interruption in this or any future turn."

Suppressing mid-response meta-commentary is reasonable. Extending it to any future turn means a
user who later asks directly whether a model switch occurred gets a model instructed to withhold
it. Suggest scoping the clause to the current response.

What Should Happen?

Setting CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK should disable all refusal-triggered model
substitution. Today it disables only the visible lane, and doing so is precisely what arms the
silent lane, because the silent rearm is reached only when no visible fallback is armed.

Suggested fix

Have ZWi short-circuit on !nV(), or give the silent path && semantics against the same gate
the visible path already uses.

Steps to Reproduce

Static defect in the shipped client. There is no runtime repro from outside: the silent lane is
additionally gated on a server-side flag (convolute_arcades), which I cannot toggle.

For Anthropic, the check is in your own source, the refusal-fallback consumer gate, which in the
2.1.220 minified build reads (nV()||Je), where nV() is the env-var check and Je is the
silent-arm boolean. The || is the bug.

To verify against the shipped binary (2.1.220, linux-x64):

B=~/.local/share/claude/versions/2.1.220

# consumer gate , the || that lets a silent arm satisfy it alone
grep -c -a 'nV()||Je' $B

# the env-var check itself
grep -c -a 'function nV(){return!Z.CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK' $B

# the silent rearm, note it consults hit(), never nV()
grep -c -a 'function ZWi(e){if(mit(e))return;return hit()' $B

# the call site, reached only when the visible lane is unarmed
grep -c -a 'refusalFallbackSilentRearm?.()' $B

All four return >= 1. Minified identifiers are build-specific, so these exact strings apply to
2.1.220 only.

Claude Model

fable 5

Is this a regression?

Not sure

Last Working Version

_No response_

Claude Code Version

2.1.220 (Claude Code)

Platform

Anthropic TeamS

Operating System

Ubuntu/Debian Linux

Terminal/Shell

WSL (Windows Subsystem for Linux)

Additional Information

What this report does not claim

Nothing here shows the silent lane activating on any account. hit() reads a server-side gate
(convolute_arcades; suppression reason silent_ab), so activation isn't determinable from the
client. This is a report about a control that doesn't work, not about observed routing.

View original on GitHub ↗