AI Engineering2026-07-258 min read

How to Handle Claude Opus 5 Refusals in Production: stop_reason, stop_details, and the Fallbacks Parameter

Claude Opus 5's safety classifiers can decline requests with stop_reason: refusal, even on legitimate security work. How to handle refusals in the Claude API, use the new fallbacks parameter, and design pipelines that degrade gracefully.

Varun Raj Manoharan
Varun Raj Manoharan
Claude Opus 5AnthropicClaude APIReliabilitySafetyPython

There's a class of production incident that only exists with frontier models: the request that succeeds at the HTTP layer and fails at the content layer. Claude Opus 5, which launched this week, makes this class bigger and more structured at the same time. It ships with real-time cybersecurity safety classifiers, and a request they decline comes back as a 200 with stop_reason: "refusal". Sometimes the content array is empty. Sometimes there's partial output, if the classifier fired mid-stream.

We do a lot of work for security-adjacent clients: SOC tooling documentation, compliance automation, log-analysis agents. That's exactly the neighborhood where false positives live. So before rolling any client onto Opus 5, I built the refusal-handling layer properly. Here's what the Claude API gives you and how we wired it.

Check stop_reason before reading content

The crash we saw within an hour of testing: code that reads response.content[0].text unconditionally. On a pre-output refusal, content is empty and that line throws an IndexError, which your error tracker then miscategorizes as a parsing bug. Every Opus 5 call site needs this branch:

Python
response = client.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    messages=messages,
)

if response.stop_reason == "refusal":
    handle_refusal(response)
else:
    text = next(b.text for b in response.content if b.type == "text")

Refusals carry a stop_details object with structured metadata: a category (such as "cyber") and an explanation. Treat it as informational rather than as the branch condition, because stop_details can be sparse and the reliable signal is stop_reason itself. Do log the category, though. It's the difference between knowing the classifier flagged offensive-security content and guessing.

Two billing details worth knowing. A refusal that fires before any output costs nothing. A mid-stream refusal bills the partial output that already streamed, and that partial output should be discarded rather than shown to users as a complete answer.

The Claude Opus 5 fallbacks parameter

Before Opus 5, handling a false-positive refusal meant catching it and re-sending the whole conversation to another model yourself: more code, another round trip, a cold prompt cache on the second model. Opus 5 introduces a server-side fallbacks parameter (beta) that does the retry inside a single API call:

Python
response = client.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    fallbacks="default",
    messages=messages,
)

With fallbacks: "default", a request the classifiers decline is automatically re-run on a fallback model, and you get that model's answer back in the same response. Your application sees one call and one result. A stop_reason: "refusal" on the final response now means something stronger: the fallback path declined too, so this is a genuine content decision rather than a transient classifier disagreement.

Our policy after a week of testing is fallbacks: "default" on every route, with the response's model field logged so we can see when a fallback actually served the answer. The fallback rate on our benign security-documentation workload has been a fraction of a percent. But a fraction of a percent of an unattended overnight pipeline is the difference between a finished job and a 3 a.m. page.

Design the terminal case per surface

Fallbacks reduce refusals without eliminating them, so the pipeline still needs a terminal branch. What goes there is a product decision. Our defaults:

  • Interactive tools: surface the refusal honestly. "The model declined this request (category: cyber)" with a link to our escalation channel. Never silently retry the same prompt in a loop; it wastes money and it doesn't work.
  • Batch pipelines: quarantine the item with its stop_details, continue the batch, and report refusal counts in the run summary. One refused document should never kill a 10,000-document job.
  • Agent loops: feed the refusal back as context and let the agent route around it. Told that a sub-task was declined, Opus 5 reformulates or flags the sub-task for a human instead of stalling.

Legitimate security work and the Cyber Verification Program

The classifiers target offensive-cyber capability, but defensive work lives next door, and adjacency is where false positives come from. Two mitigations that actually help.

Context in the prompt matters more than you'd expect. Our SOC-documentation agent refuses measurably less when the system prompt states the defensive context, the authorization, and the deliverable plainly.

And Anthropic runs a Cyber Verification Program for organizations doing legitimate security work. If that's your business, apply rather than engineering around refusals forever. Anthropic's Mythos-class models exist for exactly this population, but access is invitation-only through Project Glasswing; verification is the path most teams can take today.

The whole layer, in thirty lines

Python
def resilient_call(messages):
    response = client.messages.create(
        model="claude-opus-5",
        max_tokens=16000,
        fallbacks="default",
        messages=messages,
    )
    if response.stop_reason == "refusal":
        category = response.stop_details.category if response.stop_details else None
        raise ContentDeclined(category=category, request_id=response._request_id)
    return response

Written once, imported everywhere. The teams that get burned by model safety layers aren't the ones doing sketchy work. They're the ones who assumed a 200 always contains an answer. Opus 5 makes the contract explicit. Check stop_reason, opt into fallbacks, design the terminal case, and the classifiers become a logged, budgeted, monitored part of your system instead of a mystery outage.

Available for new projects

Let's build something great.

Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.