Refusals and fallback
How Claude Fable and Claude Opus models return classifier refusals and how to retry refused requests on a fallback model.
Claude Fable 5.1, Claude Fable 5, and Claude Opus 5 include safety classifiers that can decline a request. When that happens, you receive a normal response, not an error, with stop_reason: "refusal". Its stop_details.category names the policy area (see What a refusal looks like). You can usually still get an answer by sending the same request to another Claude model. This page shows you how to recognize a refusal and how to set up that retry.
Read this page when you build on any of these models and want declined requests to fall through to another model automatically. It also applies when you have seen "refusal" in a response and want to know what to do next.
Related pages:
- Stop reasons and fallback: the full list of
stop_reasonvalues. - Fallback credit: how to avoid paying the prompt-cache cost twice when you build the retry yourself.
- SDK middleware: the SDK helper that wraps all of this.
- Fallback and billing cookbook: a worked end-to-end example.
The simplest setup, in beta on the Claude API: set fallbacks to "default", and the API retries a declined request on the fallback model Anthropic recommends for its refusal category. For categories with no recommended fallback, the refusal stands.
client = Anthropic()
response = client.beta.messages.create(
model="claude-fable-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
fallbacks="default",
betas=["server-side-fallback-2026-07-01"],
)
print(response.model)The following sections cover what a refusal response contains, when to use server-side or client-side fallback, and how each is billed.
What a refusal looks like
A refusal is a successful HTTP 200 response with stop_reason: "refusal":
{
"id": "msg_01XFUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-fable-5",
"content": [],
"stop_reason": "refusal",
"stop_details": {
"type": "refusal",
"category": "cyber",
"explanation": "This request was declined because it could enable cyber harm."
},
"usage": {
"input_tokens": 412,
"output_tokens": 0
}
}The stop_details object explains the decline:
category: names the policy area that triggered the classifier.explanation: a human-readable description. The text is not stable, so display it rather than parse it.recommended_model: present only on requests that setfallbacks(server-side fallback, beta). It names a model to retry directly when the API skipped the fallback attempt (for example, the fallback model was rate limited), and isnullotherwise. It's a hint, not a guarantee.categoryandexplanationare bothnullwhen the refusal does not map to a named category. Thatnullis a normal, permanent value, not a placeholder.stop_detailsitself isnullfor every stop reason other thanrefusal.
category | What it means |
|---|---|
"cyber" | The request could enable cyber harm, such as malware or exploit development. Benign cybersecurity work can also trigger this category. |
"bio" | The request could enable biological harm, such as dangerous lab methods. Beneficial life sciences work can also trigger this category. |
"frontier_llm" | The request could assist the development of competing AI models, which is restricted under Anthropic's commercial terms. Benign machine learning work can also trigger this category. |
"reasoning_extraction" | The request asks the model to reproduce its internal reasoning in the response text. To get reasoning in a structured form instead, use adaptive thinking. |
"general_harms" | The request falls under a usage-policy area outside the four named categories. Benign work can also trigger this category. |
A refusal can arrive before any output, or mid-stream after partial output. In either case, treat any partial output as incomplete and discard it.
Picking a fallback approach
There are three ways to retry a refused request on another model. The right one depends on where you are running and how much control you need.
| Your situation | Use | Why |
|---|---|---|
| Claude API, simplest setup | Server-side fallback | One request, one response. The API handles the retry. |
| Any platform, using an Anthropic SDK | The SDK middleware | Configure once on the client. Retries happen automatically. |
| Raw HTTP or custom retry logic | A manual retry with fallback credit | Full control. Fallback credit keeps the cost down. |
Server-side fallback and the SDK middleware apply fallback credit for you. You only need the Fallback credit page when you build the retry yourself.
Server-side fallback
Server-side fallback retries a refused request inside a single API call. In the default mode, when the primary model declines and the refusal category has a recommended fallback, the API runs the same request on the model Anthropic recommends for that category. You can instead name up to three fallback models of your own. Either way, you get back one response that names the model that answered, so your user gets an answer in one round trip.
Making the request
Set the fallbacks parameter to the string "default" and send the server-side-fallback-2026-07-01 beta header. The API then applies the requested model's server-defined default routing, which selects a recommended fallback model based on the refusal category the classifier reports, so refused requests are served without you maintaining a model list as recommendations change.
Default routing never draws the up-front oversized-image rejection for models you did not choose: a routed model that would resize an image marked "oversized_image": "error" is dropped from the routing instead, so a marked image is never served resized.
client = Anthropic()
response = client.beta.messages.create(
model="claude-fable-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
fallbacks="default",
betas=["server-side-fallback-2026-07-01"],
)
# A fallback_message entry in usage.iterations means a fallback model ran;
# pair it with stop_reason to confirm the fallback served the response.
fallback_ran = any(
iteration.type == "fallback_message"
for iteration in response.usage.iterations or []