Serverseitige Fallbacks & Fallback Credit
Vor Opus 5 war eine Claude-Refusal dein Problem. Der Klassifikator lehnte ab, du bekamst stop_reason: "refusal" mit fröhlichem HTTP 200 zurück, und jetzt gehörte dir der Retry: ein anderes Modell wählen, die volle History erneut senden, zusehen, wie dein Prompt-Cache dahinschmilzt, weil das neue Modell einen anderen Cache-Namespace hat, und der Finanzabteilung erklären, warum dieselbe Konversation zweimal abgerechnet wurde.
Der Opus-5-Launch (24. Juli 2026) brachte zwei verwandte Betas, die das alles in einen API-Aufruf zusammenfassen:
- Server-side fallback (
server-side-fallback-2026-07-01) — setzefallbacks: "default", und die API wiederholt die abgelehnte Anfrage auf einem Modell, das Anthropic für die Refusal-Kategorie wählt, im selben Round-Trip. Du kannst auch bis zu drei eigene Ziele benennen. - Fallback credit (
fallback-credit-2026-07-01) — ein einmaliges Credit-Token, das jeder Refusal beigefügt wird und beim Echo auf dem Retry den Retry so bepreist, als wäre die Konversation von Anfang an auf dem Fallback-Modell gelaufen. Cache-Writes auf dem neuen Modell werden zu Cache-Reads.
Die zwei Betas sind unabhängig — du kannst Fallback Credit allein nutzen, wenn du schon clientseitige Retry-Logik hast — aber der Sinn des Releases ist, dass du das fast nie brauchen solltest. Diese Seite führt dich durch beide, vom Copy-Paste-Einzeiler bis zu den Randfällen, die in Prod beißen (Streaming mitten in tool_use, Sticky Routing, output_config.format sperrt die Continuation-Shape aus).
- Wie eine Refusal auf der Leitung wirklich aussieht (JSON, fünf Stop-Kategorien, wann Tokens abgerechnet werden)
- Die drei Fallback-Wege (serverseitig / SDK-Middleware / manuelles rohes HTTP) und wann welcher richtig ist
- Der Einzeiler: fallbacks: 'default' plus Beta-Header, und was die Response-Form ergänzt
- Explizite Liste vs. Default-Modus, allowed_fallback_models, und warum die Reihenfolge zählt
- Wie Fallback Credit dich davor bewahrt, den Prompt-Cache doppelt zu bezahlen — das Token, die zwei Retry-Body-Formen, und was usage.iterations zeigen soll
- Die 3-Sprossen-Ablehnungsleiter, die jeder manuelle Retry implementieren muss (Continuation → unveränderter Body → Token aufgeben)
- Wo es NICHT funktioniert: Message Batches, Bedrock/GCP/Foundry-Lücken, Sonnet 5, Streaming-Refusals mitten in tool_use, output_config.format + Server-Tools
★ Insight ─────────────────────────────────────
Zwei Anthropic-spezifische Fingerabdrücke solltest du dir merken. Erstens, eine Klassifikator-Refusal ist ein 200 mit stop_reason: "refusal" — kein 4xx. Wenn dein Fehler-Handler Non-2xx als „retry" behandelt, ignorierst du Refusals still; wenn er 200 als „success" behandelt, zeigst du still leeren Content. Weder das eine noch das andere ist gewollt. Zweitens, Prompt-Caches sind pro Modell, also zahlt ein naiver Retry auf einem anderen Claude-Modell die Cache-Write-Kosten immer von vorn, selbst wenn das Konversations-Präfix byte-identisch ist. Das Credit-Token schließt genau dieses Loch — und deshalb existiert fallback-credit als separate Beta neben Server-Side Fallback.
─────────────────────────────────────────────────
Wie eine Refusal wirklich aussieht
Eine Klassifikator-Refusal ist eine normale Message-Response mit leerem content-Array und 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
}
}
stop_details.category ist einer von fünf Werten. Zwei sind null, wenn die Refusal keiner benannten Kategorie zuordenbar ist (permanentes null, kein Platzhalter):
category | Was sie ausgelöst hat |
|---|---|
"cyber" | Anfrage könnte Cyber-Schaden ermöglichen (Malware, Exploit-Entwicklung). Auch gutartige Cybersecurity-Arbeit kann sie auslösen. |
"bio" | Anfrage könnte biologischen Schaden ermöglichen. Auch nützliche Lebenswissenschafts-Arbeit kann sie auslösen. |
"frontier_llm" | Anfrage könnte konkurrierende KI-Modellentwicklung unterstützen, per Anthropic-Handelsbedingungen eingeschränkt. |
"reasoning_extraction" | Anfrage verlangt, dass das Modell sein internes Reasoning im Antworttext reproduziert. Nutze adaptive thinking, um Reasoning in strukturierter Form zu bekommen. |
"general_harms" | Diverse Schadensbereiche; gutartige Arbeit stolpert gelegentlich hierüber. |
Eine Refusal, die vor jeder Ausgabe ankommt, wird nicht abgerechnet (ihre Tokens erscheinen in usage, werden aber nicht belastet); sie zählt weiterhin gegen deine Rate Limits. Eine Mid-Stream-Refusal rechnet Input und die bereits gestreamte Ausgabe zu Normaltarif ab. In beiden Fällen: jede Teilausgabe als unvollständig behandeln und verwerfen — der Safety-Klassifikator hat auf die eigene Trajektorie des Modells ausgelöst.
Der explanation-String ist zwischen Versionen nicht stabil. Anzeigen, nicht parsen.
Fallback-Ansatz wählen
Es gibt drei Varianten. Wähle die Zeile, die zu dir passt:
| Deine Situation | Nutze | Warum |
|---|---|---|
| Claude API, willst das Einfachste | Server-Side Fallback mit fallbacks: "default" | Eine Anfrage, eine Antwort. Die API wählt den Fallback und wendet Credit für dich an. |
| Beliebige Plattform (Bedrock, Vertex, Foundry), Anthropic-SDK | SDK-Middleware (BetaRefusalFallbackMiddleware) | Einmal auf dem Client konfigurieren. Retries + Credit automatisch. Der einzige Weg auf Bedrock / Vertex / Foundry heute. |
| Rohes HTTP, eigene Retry-Logik oder Nicht-Anthropic-SDK | Manueller Retry mit fallback-credit-2026-07-01-Header | Volle Kontrolle. Du implementierst die 3-Sprossen-Leiter selbst. |
Server-Side Fallback und die SDK-Middleware wenden Fallback Credit für dich an. Über den Credit-Token-Tanz musst du nur nachdenken, wenn du den Retry selbst baust.
Der Einzeiler: fallbacks: "default"
Das ganze Feature in einer Anfrage:
Server-Side Fallback im Default-Modus
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: server-side-fallback-2026-07-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-fable-5",
"max_tokens": 1024,
"fallbacks": "default",
"messages": [{"role": "user", "content": "Hello, Claude"}]
}'Wenn Fable 5 ablehnt und die Refusal-Kategorie einen Anthropic-empfohlenen Fallback hat, läuft die API dieselbe Anfrage auf jenem Modell im selben Call. Du bekommst eine Antwort, und das Top-Level-Feld model nennt das Modell, das tatsächlich geantwortet hat. Hat die Kategorie keinen empfohlenen Fallback, bleibt die Refusal stehen und du bekommst sie genau so zurück, als wäre fallbacks nicht gesetzt.
Was „default" wirklich tut: die API liest die servergesteuerte Routing-Tabelle des angeforderten Modells und wählt einen Fallback anhand der Refusal-Kategorie. Wenn Anthropic diese Tabelle aktualisiert (einen neuen Fallback für eine Kategorie ergänzt, Opus 5 als Default-Ziel von Fable 5 heraufsetzt usw.), bekommst du das neue Routing gratis. Das ist der Pitch: keine Fallback-Modell-Liste mehr pflegen, die in einem Monat falsch ist.
Die explizite Liste, wenn du pinnen musst
Willst du das Routing selbst steuern, übergib statt "default" eine Liste. Bis zu drei Einträge, in Reihenfolge probiert:
response = client.beta.messages.create(
model="claude-fable-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
fallbacks=[
{"model": "claude-opus-5"}, # try Opus 5 first
{"model": "claude-opus-4-8"}, # then Opus 4.8
],
betas=["server-side-fallback-2026-07-01"],
)
Die Regeln, über die du stolperst, wenn du sie nicht liest:
- Jedes Ziel muss ein erlaubter Fallback für das angeforderte Modell sein. Die Liste der erlaubten Ziele wird als
allowed_fallback_modelsauf dem Modell-Eintrag in der Models API veröffentlicht, wenn der Beta-Headerserver-side-fallback-2026-07-01gesetzt ist. (Für Claude Fable 5 ist die Liste zum Redaktionsschlussclaude-opus-4-8undclaude-opus-5.) - Einträge müssen sich voneinander und vom angeforderten Modell unterscheiden.
- Jeder Eintrag kann
max_tokens,thinking,output_configundspeednur für diesen Versuch überschreiben. So sagst du „auf dem Fallback niedrigeren Effort fahren", ohne die Hauptanfrage anzufassen. - Die Anfrage muss als Direktanfrage an jedes benannte Modell gültig sein. Wenn ein Fallback ein Feature nicht unterstützt, das die Anfrage nutzt (z. B. eine Beta, die das Fallback-Modell nicht akzeptiert), lehnt die API die ganze Anfrage vorne ab, nicht nur den Fallback-Versuch.
- Nur Klassifikator-Refusals lösen den Fallback aus. Rate Limits, Overloads und Serverfehler auf dem angeforderten Modell erreichen dich unverändert.
Der "default"-Modus funktioniert nur unter server-side-fallback-2026-07-01. Die explizite Listenform funktioniert auch unter dem älteren server-side-fallback-2026-06-01-Header.
Was die Response enthält
Die Response ist eine normale Message mit zwei Ergänzungen:
- Das Top-Level-Feld
modelnennt das Modell, das die zurückgegebene Message produziert hat (angefordert oder Fallback). - Ein
fallback-Content-Block markiert jeden Punkt, an dem die Ausgabe eines Modells der des nächsten weicht:{"type": "fallback", "from": {"model": ...}, "to": {"model": ...}}. Bei Refusal-vor-Output ist dieser Block der erste Content-Block; bei Mid-Stream-Fallback erscheint er am Übergabepunkt. usage.iterationsprotokolliert jeden Versuch. Ein Modell, das abgelehnt hat, erscheint alsmessage-Eintrag (Tokens gemeldet, aber nicht belastet); das Modell, das den Turn bediente, erscheint alsfallback_message-Eintrag.
Beispiel nach einer Refusal vor jeder Ausgabe, wenn Default-Routing Opus 4.8 wählt:
{
"id": "msg_01XFUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-opus-4-8",
"content": [
{ "type": "fallback", "from": { "model": "claude-fable-5" }, "to": { "model": "claude-opus-4-8" } },
{ "type": "text", "text": "Hi! How can I help you today?" }
],
"stop_reason": "end_turn",
"stop_details": null,
"usage": {
"input_tokens": 412,
"output_tokens": 264,
"iterations": [
{ "type": "message", "model": "claude-fable-5", "input_tokens": 535, "output_tokens": 0 },
{ "type": "fallback_message", "model": "claude-opus-4-8", "input_tokens": 412, "output_tokens": 264 }
]
}
}
Wenn jedes Modell in der Kette ablehnt, ist die Antwort die Refusal des letzten Modells, mit message-Einträgen für jeden früheren Hop und einem fallback_message-Eintrag für das letzte.
Die Konversation fortsetzen
Beim nächsten Turn den Assistant-Content so echoen, wie du ihn erhalten hast. Nach einem Mid-Output-Fallback kann der content, den du zurückbekommen hast, Blöcke enthalten, die das ablehnende Modell vor der Übergabe produziert hat. Was behalten und was verwerfen:
| Block-Typ | Beim nächsten Turn |
|---|---|
fallback | Behalten, genau an der Stelle, an der er auftauchte. Seine Position wird zur Validierung der umliegenden Thinking-Blöcke genutzt. Verschieben oder Weglassen → 400. |
text | Behalten. |
Jeder Block nach dem letzten fallback-Block | Behalten. |
thinking, redacted_thinking, connector_text vor dem letzten fallback | Verwerfen. |
Clientseitige tool_use-Blöcke vor dem letzten fallback | Verwerfen. |
server_tool_use vor dem letzten fallback | Behalten, wenn mit Result gepaart. Verwerfen, wenn kein passendes Result. |
Das mentale Modell: Alles, was auf dem Fallback-Modell lief, bleibt; die unbelegte Zwischenarbeit des ablehnenden Modells fliegt raus.
Sticky Routing
Sobald eine Konversation ins Fallback gefallen ist, merkt sich die API das. Spätere Requests für dieselbe Konversation, die ebenfalls einen fallbacks-Parameter enthalten, gehen direkt auf das Fallback-Modell und überspringen das angeforderte komplett. Das erspart dir, in einer Session, die immer wieder ablehnen würde, bei jedem Follow-up die Refusal-Steuer zu zahlen.
Merkenswerte Eigenschaften:
- Aufbewahrt für ~1 Stunde, auf deine Organisation begrenzt.
- Gespeichert als Content-Hash des Konversations-Präfix + das Modell, das bediente. Der Nachrichteninhalt selbst wird nicht serverseitig gespeichert.
- Best Effort — dein Code muss weiterhin damit umgehen, dass das angeforderte Modell jederzeit wieder versucht wird.
- Ein per Sticky bedienter Turn hat keinen
fallback-Content-Block (nichts hat abgelehnt in diesem Turn). Erkenne ihn am Vorhandensein einesfallback_messageinusage.iterations, am Fehlen einesmessage-Eintrags für das angeforderte Modell und am Response-Feldmodel.
Beim Streaming wird die Routing-Entscheidung vor dem Öffnen des Streams getroffen, message_start trägt also bereits die ID des Fallback-Modells.
Streaming-Verhalten
Der Retry passiert auf demselben Stream — nichts, was du schon empfangen hast, wird invalidiert.
Refusal vor jeder Ausgabe
message_startnennt das Fallback-Modell.- Der
fallback-Block ist der erste Content-Block. - Time to First Byte schließt den abgelehnten Versuch ein (weil
message_startauf den Start des Fallbacks wartet).
Refusal mitten in der Ausgabe
- Der aktuell offene Content-Block schließt.
- Ein
fallback-Block (content_block_start+content_block_stop, keine Deltas) markiert die Grenze. - Das Fallback-Modell setzt aus der Teilausgabe fort. Nur
text-Blöcke der Teilausgabe werden als Kontext an das Fallback-Modell übergeben; andere Block-Typen bleiben imcontent, werden vom Fallback aber nicht gesehen. message_starthat bereits das angeforderte Modell benannt, also lies das bedienende Modell aus demto.modeldesfallback-Blocks und aus demfallback_message-Eintrag inusage.iterationsdes finalenmessage_delta.
Non-Streaming, Mid-Output-Refusal: die Antwort lässt die Teilausgabe des abgelehnten Modells weg und das Fallback antwortet von Grund auf. Das Ergebnis sieht aus wie eine Refusal-vor-Output — fallback-Block zuerst — mit den Tokens des abgelehnten Versuchs weiterhin in usage.iterations protokolliert. Das ist ein echter Verhaltensunterschied zum Streaming; Sizing-Tests auf dem Stream können die Kosten unterschätzen, wenn du auf Non-Streaming umschaltest.
Fallback Credit: die unsichtbare Neubepreisung
Prompt-Caches sind pro Modell. Wenn Fable 5 400k Tokens deines Konversations-Präfix gecacht hat und ablehnt, muss ein naiver Retry auf Opus 5 alle 400k von vorn in den Cache von Opus 5 schreiben — und Cache-Writes kosten mehr als Cache-Reads. Fallback Credit nimmt diese Zusatzkosten weg. Die Refusal trägt ein einmaliges Credit-Token, du echost das Token auf dem Retry, und der Retry wird bepreist, als wäre die Konversation von Anfang an auf dem Fallback-Modell gelaufen.
Server-Side Fallback und die SDK-Middleware wenden Credit automatisch an. Über das Token musst du nur selbst nachdenken, wenn du den Retry auf rohem HTTP baust.
Der vierstufige manuelle Ablauf
- Sende die erste Anfrage mit anthropic-beta: fallback-credit-2026-07-01. (server-side-fallback-2026-07-01 gewährt dieselben Felder, und der ältere fallback-credit-2026-06-01-Header wird weiter akzeptiert.)
- Bei einer Refusal enthält stop_details fallback_credit_token (opaker String) und fallback_has_prefill_claim (Boolean). Beide sind null, wenn für die Refusal kein Credit verfügbar ist.
- Starte vom abgelehnten Request-Body. Setze model auf das Fallback-Modell, füge das Token als Top-Level fallback_credit_token hinzu. Wenn fallback_has_prefill_claim nicht false ist, hänge genau eine Assistant-Message an, die den Inhalt der abgelehnten Response echot — der Retry setzt dort fort, wo das abgelehnte Modell aufhörte, und abgeschlossene Server-Tool-Calls werden nicht erneut ausgeführt. Wenn false, sende den unveränderten Body.
- Der Retry muss den Header fallback-credit-2026-07-01 tragen, um das Token einzulösen. Beta-Header müssen zwischen den beiden Anfragen matchen (siehe Strict-Match-Regeln unten).
Die Ablehnungsleiter, die jeder manuelle Retry braucht
Die meisten Retries lösen beim ersten Versuch ein. Wenn nicht, gibt die API einen 400 zurück, der sagt, was als Nächstes zu versuchen ist. Implementiere alle drei Sprossen:
- Häufigste Ursache: output_config.format oder ein tool_choice, das Tool-Use erzwingt, schließt die Continuation-Shape aus. Angehängte Assistant-Message weglassen; Token behalten.
- Das Token selbst wurde abgelehnt. Retry ohne Token. Der Credit ist verwirkt; der Retry selbst geht durch.
- Ein tokenloser Retry führt diese Tools erneut aus und rechnet erneut ab. Reiche die Kosten oder den Fehler an deinen Aufrufer weiter.
- "redemption temporarily unavailable" ist ein transienter Fehler, KEIN Urteil über deine Retry-Shape. Dieselbe Anfrage mit demselben Token innerhalb des 5-Minuten-Fensters wiederholen. Nicht die Leiter absteigen.
Felder, die exakt matchen müssen (die Strict-Match-Regeln)
Beim Einlösen wird dein Retry mit der abgelehnten Anfrage verglichen. Jedes Feld, das den Prompt formt, muss matchen:
| Regel | Felder |
|---|---|
| Muss exakt matchen | system, messages, tools, tool_choice, thinking, cache_control, und (wenn genutzt) output_config, mcp_servers, context_management, container |
| Darf sich beim Retry ändern | model, max_tokens, stop_sequences, temperature, top_p, top_k, stream, metadata, service_tier |
Die Continuation-Shape ist die eine Ausnahme vom messages-Match: sie fügt am Ende von messages genau eine Assistant-Message hinzu.
Zwei subtile Fallen:
- Beta-Header müssen auch matchen. Ein Beta-Header auf einer der beiden Anfragen, aber nicht auf der anderen, kann den Match sprengen, selbst wenn die Bodies identisch sind. Der 400 sagt
request body ... does not match, was nach Body-Diff klingt, aber ein Header-Diff ist. Zwei Familien sind ausgenommen:server-side-fallback-*(beim Retry zusammen mit demfallbacks-Param weglassen) undfallback-credit-*(auf beiden behalten). thinking- oderredacted_thinking-Blöcke aus früheren Turns beim Retry NICHT strippen, auch wenn ein einfacher tokenloser Retry das normalerweise tut. Der Body muss der abgelehnten Anfrage entsprechen; der Server kümmert sich um diese Blöcke selbst.
Prüfen, dass der Credit tatsächlich griff
Die Erstattung ist im usage des Retry sichtbar. Verglichen mit dem, was dieselbe Anfrage ohne Token melden würde, ist cache_creation_input_tokens niedriger und cache_read_input_tokens um denselben Betrag höher. Ein Delta von null bedeutet, das Token wurde honoriert, aber es gab nichts neu zu bepreisen (z. B. weil der Cache des Retry-Modells schon warm war).
Token-Scope und -Lebensdauer
- Löst nur ein aus derselben Organisation und dem Workspace, der die Refusal empfangen hat (auch auf Foundry). Auf Bedrock und Vertex, die keine Workspaces haben, ist das Token an die Caller-Identität der Plattform gebunden.
- Läuft 5 Minuten nach der Refusal ab. Danach ohne Token wiederholen.
- Zustandslos — der Server speichert nichts darüber, und es gibt keinen Endpunkt, um es zu inspizieren oder zurückzuziehen.
Wo es nicht (oder anders) funktioniert
- Der fallbacks-Parameter wird auf der Message-Batches-API nicht unterstützt (ein Batch-Item, das ihn enthält, kommt als errored zurück). Refusals in Message Batches prägen auch keine Credit-Token, und ein auf einem Batch-Request übergebenes Token wird akzeptiert, aber ignoriert. Nach Abschluss des Batches auf clientseitigen Retry zurückfallen.
- Der fallbacks-Parameter ist auf Amazon Bedrock, Google Cloud und Microsoft Foundry nicht verfügbar — nutze stattdessen die SDK-Middleware. Fallback Credit selbst funktioniert auf allen vier Plattformen.
- Nur Fable 5 und Opus 5 enthalten derzeit den Klassifikator, der Klassifikator-Refusals erzeugt. Sonnet-5-Refusals kommen als normale End-Turn-Responses ohne stop_reason: 'refusal' an, und es gibt nichts, aus dem zurückgefallen werden könnte.
- Genau dieser Fall (Streaming, Refusal während eines unfertigen Client- / Server- / MCP-Tool-Calls) wird NICHT serverseitig wiederholt. Die Refusal wird direkt zurückgegeben. Wenn fallback-credit-2026-07-01 gesetzt ist, trägt sie weiterhin ein Credit-Token, das per Fortsetzung der Teilausgabe einlösbar ist. Non-Streaming-Requests sind nicht betroffen.
- Die eine Kombi, in der das Credit-Token in keiner Body-Shape einlösbar ist: die Continuation-Shape ist durch format/tool_choice ausgeschlossen, und der unveränderte Body ist ausgeschlossen, weil abgeschlossene Server-Tools erneut laufen und erneut abgerechnet würden. Token verwerfen; ohne Token wiederholen UND die Kosten an den Aufrufer durchreichen.
- Wenn das Fallback-Modell rate-limitiert oder überlastet ist, wird der Fallback-Versuch nicht unternommen und die vorangehende Refusal wird stattdessen zurückgegeben. stop_details.recommended_model nennt ein Modell für einen direkten Retry (Hinweis, keine Garantie; null wenn nicht verfügbar). Dimensioniere Fallback-Rate-Limits für dein erwartetes Refusal-Volumen, sonst degradieren Fallbacks unter Last zu Refusals.
Ein pragmatisches Setup für eine Claude-Produktions-App
- Null-Aufwand-Schutz für die Kategorien, für die Anthropic Fallbacks empfohlen hat. Es ist ein Superset des manuellen Ansatzes, weil die Routing-Tabelle sich automatisch aktualisiert.
- BetaRefusalFallbackMiddleware mit deiner Fallback-Liste einmal auf dem Client setzen. Einen BetaFallbackState über Anfragen derselben Konversation teilen, damit Follow-ups auf dem akzeptierenden Modell gepinnt bleiben. Die Middleware sendet fallback-credit-2026-07-01 auf jeder Anfrage, die sie behandelt.
- Sticky Routing heißt, Turn N+1 in einer Session kann still auf einem anderen Modell laufen als Turn N. Wenn du Kosten oder Qualität in Analytics dem angeforderten Modell zuordnest, liegst du falsch. response.model lesen, und wenn usage.iterations einen fallback_message-Eintrag enthält, den ebenfalls loggen.
- Das Feld stop_details.category ist das nächste, was du an Signal hast, dass deine User an Policy-Wände laufen. Eine steigende 'cyber'-Kategorie heißt nicht automatisch bösartige User — Cybersecurity-Arbeit stolpert legitim darüber — aber sie sagt dir, wo eine UI-Notiz oder ein kategoriespezifischer Fallback hin muss.
- Der eine 400-Fall, der das trifft: Refusal nach bereits gelaufenen Server-Tools + output_config.format oder erzwungenes tool_choice. Das Token ist uneinlösbar, und ein naiver Retry führt web_search / code_execution / MCP-Tool-Calls erneut aus (und rechnet erneut ab). Fehler durchreichen.
Wie sich das zu anderen Anbietern verhält
| Anbieter | Automatische Refusal → Fallback in einem API-Call? |
|---|---|
| Anthropic Claude Fable 5 / Opus 5 | Ja — fallbacks: "default" + Credit-Token. Sticky Routing zieht Follow-ups mit. |
| Anthropic Claude Opus 4.8 | War das Zielmodell der reinen Credit-Token-Variante (Beta Juni 2026). Server-Side-Default-Modus landete mit Opus 5. |
| OpenAI GPT-5 / 6 | Kein First-Party-Server-Side-Fallback. Du erkennst ein refusal-finish_reason selbst und wiederholst auf einem anderen Modell clientseitig; die Responses-API veröffentlicht kein Äquivalent zu allowed_fallback_models. |
| Google Gemini 3 | Refusals tauchen als SAFETY-Block-Reasons auf; Retry ist clientseitig gegen ein anderes Modell in der Familie. |
| AI Gateways (LiteLLM, Portkey, OpenRouter) | Provider-agnostischer Router-Level-Fallback existiert, wird aber pro Versuch unabhängig abgerechnet — kein Per-Provider-Cache-Credit-Äquivalent. Siehe AI Gateways. |
Cross-Model-Harnesses können das Credit-Token weiterhin nutzen: es ist modellspezifisch, aber das Konzept (ein opakes Token auf dem Retry echoen, neu bepreist werden) lässt sich pro Anbieter feature-detecten.
Häufige Fehlerbilder und was sie bedeuten
- Du bekommst ein leeres
content-Array zurück und deine UI zeigt eine leere Nachricht. Du hast vor dem Rendernstop_reason: "refusal"nicht geprüft. Erkenne die Refusal und zeige entweder eine kategoriespezifische Nachricht oder verdrahte Fallbacks. - Dein Retry gibt weiterhin 400 mit
request body ... does not match. Wahrscheinlich ein Header-Mismatch. Diff jedenanthropic-beta-Header zwischen den beiden Anfragen, nicht nur den Body. - Du nutzt die SDK-Middleware und siehst dasselbe Modell zweimal abgerechnet. Du hast vergessen, den
BetaFallbackStateüber die Anfragen derselben Konversation zu teilen. Sticky Routing braucht den State, um Follow-ups zu pinnen. - Dein Kostenreport zeigt einen dicken Sprung auf Opus 4.8, obwohl du dachtest, du seist auf Fable 5. Sticky Routing hat Follow-ups nach einer Refusal mitgezogen.
response.modelundusage.iterationsloggen, um die Aufteilung zu sehen. - Du hast den Beta-Header auf dem Retry vergessen und bekamst einen Redemption-Fail. Der Retry braucht
fallback-credit-2026-07-01, um das Token einzulösen. - Batch-Job lässt deine Fallbacks still fallen. Batches ignorieren
fallbacksund Credit-Tokens. Retry nach Batch-Abschluss machen.
Check yourself
0/7Quellen & Weiterlesen
- Refusals and fallback — Claude Platform Docs (definitive Referenz für
fallbacks,"default"-Modus, Sticky Routing und Streaming-Verhalten; enthält vollständige 8-SDK-Codebeispiele) - Fallback credit — Claude Platform Docs (Credit-Token-Flow, die zwei Body-Shapes, die Ablehnungsleiter, Strict-Match-Regeln, 5-Minuten-TTL)
- What's new in Claude Opus 5 (der Launch am 24. Juli 2026, der
"default"-Modus und Thinking-on-by-default brachte) - Claude Platform release notes (Release-Historie der
server-side-fallback-*- undfallback-credit-*-Beta-Header) - Prompt caching — Claude Platform Docs (warum Cache-Writes mehr kosten als Reads und warum Per-Model-Cache-Namespaces das Credit-Token nötig machen)
- Stop reasons and fallback — Claude Platform Docs (die vollständige Liste der
stop_reason-Werte, unter denen"refusal"einer ist) - Fallback and billing cookbook (durchgängiges Beispiel inklusive Kostenrechnung)
- Models API —
allowed_fallback_models(kanonische Quelle für erlaubte Fallback-Ziele pro Modell; Beta-Header setzen, um das Feld zu sehen) - Verwandt auf dieser Seite: Safety, refusals & fallbacks, Prompt caching, Errors and rate limits, AI gateways: LiteLLM, OpenRouter, Portkey