TroubleshootingAdvanced

How to handle a Claude Fable 5 refusal with a fallback model

Detect the refusal stop reason and opt into a server-side fallback so a declined request is re-served by another model.

9 minAdvanced

Claude Fable 5 runs safety classifiers that can decline a request. A decline comes back as a successful HTTP 200 with stop_reason set to refusal, not as an exception. If your code reads response.content directly, it will break on these. Worse, false positives can hit benign adjacent work. This guide shows how to detect the refusal and opt into a fallback model so the request is rescued automatically.

  • The Anthropic SDK with access to claude-fable-5
  • An organization with 30-day data retention (Fable 5 is not available under zero retention)
  • A fallback model in mind, typically claude-opus-4-8

Step 1: Check stop_reason before reading content

Always branch on stop_reason first. A pre-output refusal has an empty content array and is not billed. A mid-stream refusal bills the partial output, which you should discard. Reading content[0] without this guard throws on refused requests.

guarded.py
resp = client.messages.create(
    model="claude-fable-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": prompt}],
)

if resp.stop_reason == "refusal":
    print("declined:", resp.stop_details.category if resp.stop_details else None)
else:
    print(resp.content[0].text)
Terminal — a refusal is an HTTP 200
$ python guarded.py
declined: cyber
(content array was empty; this attempt was not billed)
No exception is raised; stop_reason is the signal.

Step 2: Opt into a server-side fallback

Fallbacks are opt-in. On the beta endpoint, pass the server-side-fallback beta header and a fallbacks list. On a policy decline the API runs the next model on the same request and returns its answer, with credit-style repricing applied automatically. Ship this by default in new Fable 5 code.

fallback.py
resp = client.beta.messages.create(
    model="claude-fable-5",
    max_tokens=1024,
    betas=["server-side-fallback-2026-06-01"],
    fallbacks=[{"model": "claude-opus-4-8"}],
    messages=[{"role": "user", "content": prompt}],
)

for block in resp.content:
    if block.type == "fallback":
        print(f"{block.from_.model} declined; {block.to.model} continued")
Get the header string exact
The header must be exactly server-side-fallback-2026-06-01. Other date-looking variants reject the fallbacks parameter with a 400. The parameter is also not available on the Batches API, Amazon Bedrock, Vertex AI, or Microsoft Foundry.

Step 3: Confirm which model served the answer

A fallback block in content marks each switch point. The reliable served-by signal is a fallback_message entry in usage.iterations, which also covers sticky follow-up turns that carry no block. Pair it with stop_reason, since the fallback model can itself refuse.

served_by.py
fallback_ran = any(
    entry.type == "fallback_message"
    for entry in (resp.usage.iterations or [])
)
if fallback_ran and resp.stop_reason != "refusal":
    print("served by", resp.model)
Tell the user it is on
Because false positives happen on legitimate security and life-sciences work, enabling the fallback by default keeps those requests from failing outright. Mention to the user that you turned it on, and remove it only if they decline.

Result: a benign security-tooling prompt that Fable 5 declined with a cyber category now returns a real answer. The fallbacks list re-served it on Opus 4.8 inside the same call, the fallback_message entry confirmed Opus produced the response, and the declined Fable attempt cost nothing.

Watch related tutorials

Tags
#fable#refusal#fallback#claude#errors