stop_reason: refusal
stop_reason: refusal on Claude Fable 5
A safety classifier declined the request. It is a successful HTTP 200 response, which is exactly why most integrations never notice it.
The 30-second version
Fable 5 runs safety classifiers that can decline a request. The decline is HTTP 200 with stop_reason set to refusal, an empty content array, and a stop_details object naming the category. No exception is raised. Branch on stop_reason after every call, read the category, and retry on another Claude model. The reasoning_extraction category catches teams by surprise, because prompts that ask the model to show its work trigger it.
The five refusal categories
stop_details.category names the policy area that fired. Both category and explanation can be null when the refusal maps to no named category, and that null is permanent, not a placeholder.
bio
The request could enable biological harm, such as dangerous lab methods. Beneficial life sciences work can also trigger it. In Claude Code, a bio flag on Fable 5 moves the session to Opus 5, and because Opus 5 runs its own biology classifiers with no further fallback, later bio-flagged requests end in refusals there.
cyber
The request could enable cyber harm, such as malware or exploit development. Benign cybersecurity work triggers this too. Penetration testing, CTF exercises and security tooling repositories hit it frequently, often on the first request, and that is expected routing rather than a flag on your account.
frontier_llm
The request could assist the development of competing AI models, which Anthropic's commercial terms restrict. Benign machine learning work can also trigger it, so evaluation harnesses and model-comparison tooling sometimes land here unexpectedly.
general_harms
The request touches an area determined to be harmful and does not map to one of the more specific categories. Benign work occasionally triggers it, and this is the category most likely to be resolved by rephrasing the request or adding context about intent.
reasoning_extraction
The request asks the model to reproduce its internal reasoning in the response text. Prompts, skills or harness instructions that say show your work, think step by step, or explain your reasoning trigger this category and cause elevated fallbacks to Opus 4.8. This is the most common self-inflicted refusal on Fable 5.
Fix in this order
Detection comes before mitigation. If you cannot count your refusals, you cannot tell whether a fallback is working.
- 1
2. Audit your prompts for reasoning-extraction language
Search your system prompts, skills and harness instructions for anything that tells the model to echo, transcribe or explain its internal reasoning as response text, and remove it. If you need reasoning visibility, read the structured thinking blocks from adaptive thinking instead.
- 2
1. Branch on stop_reason, not on content
Check stop_reason equal to refusal, or stop_details.type, directly after every call. Do not infer a refusal from an empty content array and do not parse stop_details.explanation, whose text is not stable and is meant for display rather than logic.
- 3
3. Configure fallback on every request path
Server-side fallback needs the fallbacks parameter plus the server-side-fallback-2026-07-01 beta header, and it is beta on the Claude API. The SDK middleware works on any platform. Whichever you pick, apply it in retry handlers and background workers too, and give subagent calls their own fallback, since the parameter does not propagate into model calls made inside tool execution.
- 4
5. Emit refusals as their own metric
A refusal is a 200, so monitoring built on error rates or 5xx responses never sees it. Emit one event per refusal and one per fallback-served response, identified by a fallback_message entry in usage.iterations, then alert on the gap between the two counts.
- 5
4. Give the fallback model real rate limit headroom
If the fallback model is itself rate limited or overloaded, the fallback attempt is skipped and the original refusal is returned instead. Size the fallback model's limits for the refusal volume you expect, or fallback silently degrades to refusal exactly when load is highest.
Not a refusal?
If your response is a real HTTP error rather than a 200, start from the status code instead. The handling is completely different.