[BUG] Refusal-fallback credited retry changes output_config.effort (high to xhigh), gets HTTP 400, then retries without the credit token
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?
When a main-loop turn on Opus 5.5 was refused by the cyber safeguard, Claude Code retried on Opus 4.8 with the fallback_credit_token from the refusal, but the retry carried a different output_config.effort than the refused request (high on the refused request, xhigh on the retry). The API rejected that retry with HTTP 400. Claude Code then retried without the token, which succeeded. That last step matches the docs' own example code for a rejected token ("forfeit it and retry without"), so the problem is the 400, not the tokenless retry, but it means the credit was forfeited. I saw this once, on 2026-09-23 (UTC). The refused work was ordinary local development/admin work; this report is only about what happened to the credit afterwards.
The Fallback credit reference says redemption "compares the retry against the refused request. Every field that shapes the prompt must match exactly", and lists output_config under "Must match exactly".
In the same session a subagent also hit a cyber refusal on Opus 5.5 at effort high. Its credited retry to Opus 4.8 kept output_config at {"effort":"high"} and was accepted with HTTP 200. So in this session, the credited retry that kept effort unchanged was accepted and the one that changed it was rejected.
Measured from an HTTPS proxy capture of the session's /v1/messages traffic (request bodies and responses read directly):
| Request | Model | output_config | fallback_credit_token | Result |
|---|---|---|---|---|
| refused turn | claude-opus-5-5 | {"effort":"high"} | not sent | 200, stop_reason: "refusal", stop_details.category: "cyber", token and fallback_has_prefill_claim: true returned |
| credited retry, sent 7 s after the refused request | claude-opus-4-8 | {"effort":"xhigh"} | sent | 400 (message below) |
| tokenless retry, 1 s later | claude-opus-4-8 | {"effort":"xhigh"} | not sent | 200, cache_creation_input_tokens: 834139, cache_read_input_tokens: 0 |
The credited retry carried x-is-refusal-fallback: true, x-cc-fallback-trigger: refusal and x-cc-fallback-category: cyber, and the session transcript records a model_refusal_fallback event from claude-opus-5-5[1m] to claude-opus-4-8 at the same moment. A later main-loop Opus 4.8 request I checked, after the tokenless retry, also sent {"effort":"xhigh"}, so my guess is that an effort chosen for the fallback model is being applied to the credited retry, which still has to match the refused request.
Possible impact (inferred, not measured): the docs say fallback credit "removes that extra cost" of writing the conversation into the new model's cache. The tokenless retry here wrote the ~834k-token prefix to the Opus 4.8 cache with no cache reads (cache_creation_input_tokens: 834139, cache_read_input_tokens: 0); the docs describe a redeemed token as showing lower cache_creation_input_tokens and higher cache_read_input_tokens by the same amount. I have not verified how that is accounted on a subscription, so I can't say what it cost.
What Should Happen?
The credited retry should send the refused request's output_config unchanged (here {"effort":"high"}, the shape that was accepted for the subagent), or whatever shape the API intends for redemption, so the token is redeemed. If Claude Code wants a different effort on the fallback model, that could apply from the next request onward.
Error Messages/Logs
HTTP 400 on the credited retry (POST /v1/messages, model claude-opus-4-8):
{"type":"error","error":{"type":"invalid_request_error","message":"fallback_credit_token: output_config.effort must be omitted from the retry to redeem the token"}}
Steps to Reproduce
- Run Claude Code 2.1.280 on Opus 5.5 with effort
high, default settings, so that a flagged turn falls back to Opus 4.8 automatically. - Route its API traffic through an HTTPS proxy you already trust and that records request bodies. Note that such a capture also records your auth header.
- Work until a main-loop turn is refused with
stop_details.category: "cyber". I can't give a reliable trigger; mine came from ordinary local development/admin work. - Compare
output_configon the refused request with the next request toclaude-opus-4-8that carriesfallback_credit_token. In my capture the main-loop retry carriedxhighinstead ofhigh, got the 400 above, and was followed immediately by a tokenless retry that succeeded.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.280 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
The API message says effort "must be omitted from the retry", while the docs say output_config must match the refused request, and the only credited retry I saw accepted carried an unchanged effort. I did not test a retry with effort omitted. I mention it because the message and the docs point in different directions, which could matter for the fix.
Both credited retries (main loop and subagent) also dropped the context-1m-2025-08-07 beta header that their refused requests had sent. The docs say "Send the same anthropic-beta headers on the retry as on the refused request" and "do not add or drop beta headers based on which model the request targets". The subagent's retry was accepted anyway, so this does not look like the cause of the 400, but it is a second difference between the two requests.
Related: #94652 (fallback-model usage billed as ordinary subscription usage; a forfeited credit could add to that, not measured) and #73833 (request to remap effort on fallback, closed as not planned).