Model fallback is not one behavior. It is a set of recovery decisions with different safety limits.
A production model fallback strategy should separate three workflows:
- Retry or equivalent failover when the request is still safe to replay.
- Cross-model fallback when another model can satisfy the same capability and quality contract.
- Stop, reconcile, or escalate when output has already reached the user or a tool side effect may have happened.
That separation matters because the fastest recovery action is not always the safest one. Replaying a failed classification request is usually low risk. Silently switching models halfway through a streamed answer or after an uncertain payment tool call is not.
This playbook turns fallback policy into three operational workflows your team can implement, test, observe, and release through a controlled production rollout.
The model fallback decision in one table
Start with the request state, not the provider name.
| Request state | Preferred workflow | Typical action | Do not do |
|---|---|---|---|
| No response bytes, transient transport error | Workflow 1 | Bounded retry, then equivalent endpoint failover | Retry without a deadline or budget |
| No response bytes, rate limit or overload | Workflow 1 | Honor retry guidance, apply jitter, then move to equivalent capacity | Create a synchronized retry storm |
| Primary target unavailable, compatible model exists | Workflow 2 | Check the fallback contract, then route to the approved alternate | Assume every model supports the same tools, schema, or context |
| Structured response fails validation | Workflow 2 | Repair once or try an approved model that meets the schema contract | Treat HTTP 200 as task success |
| Partial stream already delivered | Workflow 3 | Stop, mark partial, offer an explicit restart | Splice a second model into the same answer invisibly |
| Write-side tool may have executed | Workflow 3 | Reconcile tool state using an idempotency record | Replay the entire model-and-tool workflow automatically |
| Safety or policy classification is uncertain | Workflow 3 | Escalate or fail closed according to product policy | Lower the safety bar to preserve availability |
The core rule is simple: retry preserves the target, equivalent failover preserves the model contract, and cross-model fallback changes the contract risk. Each step needs a stronger eligibility check.
For a deeper treatment of circuit breakers, error normalization, and a provider-neutral controller, see the LLM API fallback routing playbook.
Before the workflows: define one fallback envelope
Every request should enter the routing layer with a bounded envelope. The envelope tells the system how much recovery is allowed before the request must stop.
type FallbackEnvelope = {
requestId: string;
deadlineMs: number;
maxAttempts: number;
maxAddedLatencyMs: number;
maxCostUsd?: number;
allowEquivalentFailover: boolean;
allowCrossModelFallback: boolean;
allowAfterPartialOutput: false;
sideEffectMode: "none" | "read_only" | "write_possible";
requiredCapabilities: string[];
requiredSchemaVersion?: string;
};
The values should come from the product workflow, not from a global default. A background summarization job can tolerate more latency than an interactive coding assistant. A chat answer with no tools can tolerate different recovery behavior than an agent that can deploy code or send email.
The envelope also prevents nested retries. If the SDK, application, gateway, and provider adapter all retry independently, a small incident can multiply into a large attempt burst. Pick one layer to own the total attempt budget and require every lower layer to report what it already consumed.
Turn the fallback envelope into policy as code
A type definition documents intent, but production routing needs a versioned policy that operators can review without changing application code. Keep the policy small enough to audit and specific enough to prevent a generic fallback chain from leaking into high-risk workflows.
This starter configuration separates three common route classes:
policy_version: 2026-08-02
routes:
interactive_chat:
deadline_ms: 12000
max_attempts: 2
max_added_latency_ms: 2500
allow_equivalent_failover: true
allow_cross_model_fallback: true
allow_after_partial_output: false
side_effect_mode: none
required_capabilities: [streaming]
structured_extraction:
deadline_ms: 30000
max_attempts: 3
max_added_latency_ms: 8000
allow_equivalent_failover: true
allow_cross_model_fallback: true
allow_after_partial_output: false
side_effect_mode: none
required_capabilities: [structured_output]
required_schema_version: invoice-v4
tool_agent_write:
deadline_ms: 45000
max_attempts: 2
max_added_latency_ms: 5000
allow_equivalent_failover: true
allow_cross_model_fallback: false
allow_after_partial_output: false
side_effect_mode: write_possible
required_capabilities: [tool_use]
The values above are examples, not universal thresholds. Set them from your user-facing latency objective, task economics, evaluation results, and side-effect risk. The important design choice is that the write-capable agent cannot silently switch to a behaviorally different model.
At runtime, the router should combine the policy with request state and observed failure state. A compact decision function can make the boundary testable:
type RecoveryAction =
| "retry_same_target"
| "failover_equivalent"
| "fallback_approved_model"
| "reconcile_side_effect"
| "restart_required"
| "stop";
function chooseRecovery(input: {
errorClass: string;
attemptsUsed: number;
deadlineRemainingMs: number;
partialOutput: boolean;
sideEffectState: "none" | "safe" | "uncertain";
equivalentAvailable: boolean;
approvedAlternateAvailable: boolean;
policy: FallbackEnvelope;
}): RecoveryAction {
if (input.sideEffectState === "uncertain") return "reconcile_side_effect";
if (input.partialOutput) return "restart_required";
if (input.attemptsUsed >= input.policy.maxAttempts) return "stop";
if (input.deadlineRemainingMs <= 0) return "stop";
const transient = [
"transport_transient",
"rate_limited",
"provider_overloaded",
"provider_server_error",
].includes(input.errorClass);
if (transient && input.attemptsUsed === 0) return "retry_same_target";
if (transient && input.equivalentAvailable) return "failover_equivalent";
if (
input.policy.allowCrossModelFallback &&
input.approvedAlternateAvailable
) {
return "fallback_approved_model";
}
return "stop";
}
Keep candidate selection separate from the recovery decision. chooseRecovery decides which workflow is allowed; a candidate selector then filters targets by capability, context, region, cost, and quality policy. This separation makes incident review easier because the team can distinguish “we chose the wrong recovery workflow” from “we chose the wrong alternate model.”
Version the policy and attach that version to every attempt trace. When a fallback regression appears, operators should be able to answer which policy made the decision, which candidates were eligible, and which budget remained at that moment.
Workflow 1: retry, then equivalent failover
Use this workflow when the operation is replayable and the system has not exposed partial output or entered an uncertain side-effect state.
An equivalent target is another route that preserves the important contract: same model behavior class, required capabilities, schema expectations, safety configuration, and compatible context limits. It may be a different region, deployment, provider endpoint, or capacity pool.
Step 1: normalize the failure
Map provider-specific responses into a small internal taxonomy:
transport_transientrate_limitedprovider_overloadedprovider_server_errorauthentication_or_permissioninvalid_requestdeadline_exhaustedcontract_failurepartial_outputside_effect_uncertain
Only the first four normally qualify for automatic replay. Authentication, permission, and invalid-request errors should stop because a different endpoint is unlikely to repair the request. Contract failures belong in Workflow 2. Partial output and uncertain side effects belong in Workflow 3.
Step 2: calculate the remaining budget
Before every attempt, check:
remaining time > estimated next-attempt latency + response safety margin
remaining attempts > 0
remaining added latency > 0
remaining cost budget > estimated attempt cost, when a cost ceiling exists
If any required budget is exhausted, exit instead of trying one more provider.
Step 3: retry with backoff and jitter
Use provider retry guidance when available. Otherwise, apply exponential backoff with jitter and keep the delay inside the request deadline.
function retryDelayMs(attempt: number, retryAfterMs?: number): number {
if (retryAfterMs !== undefined) return retryAfterMs;
const base = Math.min(250 * 2 ** attempt, 4_000);
const jitter = Math.random() * base * 0.3;
return Math.round(base + jitter);
}
Jitter matters because many simultaneous clients can otherwise retry on the same schedule and prolong an overload event. Your LLM rate limits guide should define how RPM, TPM, queues, concurrency, and retry budgets interact.
Step 4: move to equivalent capacity
If the same target remains unhealthy, route to an equivalent endpoint only after checking:
- The circuit is closed or half-open for a probe.
- The target supports the required input and output modes.
- The target can accept the request within its context limit.
- The target uses the expected safety and data-handling configuration.
- The attempt still fits the deadline and cost envelope.
Equivalent failover is normally less risky than changing models because it aims to preserve the response contract.
Step 5: record the recovery reason
Return a route outcome such as:
{
"workflow": "retry_equivalent_failover",
"primary_attempts": 2,
"equivalent_failover_attempts": 1,
"recovered": true,
"recovery_reason": "provider_overloaded",
"added_latency_ms": 684
}
Do not expose internal provider details to end users unless your product promises that transparency. Do preserve them in traces and operational logs.
Workflow 2: controlled cross-model fallback
Cross-model fallback is appropriate only when the alternate model has been pre-approved for the task. A model returning text is not enough; it must satisfy the workflow contract.
Step 1: create a capability contract
Define the non-negotiable requirements for each route class.
{
"route_class": "support_ticket_triage_v3",
"required": {
"input": ["text"],
"output": ["json_schema"],
"tools": [],
"minimum_context_tokens": 24000,
"schema": "triage-result-v3",
"languages": ["en", "es", "de"],
"safety_profile": "customer-support-standard"
},
"fallback_models": [
"approved-model-b",
"approved-model-c"
]
}
For tool-using routes, include tool-choice behavior, parallel tool support, argument schema handling, and whether the model reliably follows “do not call” conditions. For structured outputs, validate the actual response against the schema after every attempt.
Step 2: separate transport success from task success
An HTTP success response can still fail the product workflow. Evaluate at least three layers:
- Transport success: the provider returned a complete response.
- Contract success: the response parsed, matched the schema, and used supported tools correctly.
- Task success: the output actually completed the user’s job at an acceptable quality level.
This distinction is essential when comparing fallback candidates. A model with a high response rate but frequent schema or tool failures is not a reliable fallback.
Step 3: rank approved candidates by policy
A production router can score eligible targets using operational signals without pretending that one model is universally best.
type Candidate = {
id: string;
capabilitiesPass: boolean;
circuitOpen: boolean;
estimatedLatencyMs: number;
estimatedCostUsd: number;
recentContractSuccess: number;
recentTaskSuccess: number;
};
function eligible(candidate: Candidate, envelope: FallbackEnvelope): boolean {
return (
candidate.capabilitiesPass &&
!candidate.circuitOpen &&
candidate.estimatedLatencyMs <= envelope.maxAddedLatencyMs &&
(envelope.maxCostUsd === undefined ||
candidate.estimatedCostUsd <= envelope.maxCostUsd)
);
}
Avoid a static “primary, backup, backup” list for every task. The best fallback set for code generation may differ from the best set for extraction, translation, vision, or tool execution.
Step 4: validate the fallback output
Apply deterministic checks first:
- JSON or schema validation
- Required field checks
- Tool argument validation
- Citation or URL format checks
- Length and language constraints
- Forbidden output patterns
Then add workflow-specific quality checks. These may be lightweight rules, a task evaluator, sampled human review, or a validated judge model. If the quality gate fails, do not label the fallback as recovered.
Step 5: canary policy changes
Before expanding a new fallback model:
- Replay an offline evaluation set.
- Run shadow traffic where policy allows it.
- Enable the candidate for a small percentage of eligible failures.
- Compare contract success, task success, latency, and cost.
- Expand only if the recovery value outweighs the regression risk.
Track these measurements with an LLM API observability schema that records one route and one span per attempt.
Workflow 3: stop, reconcile, or escalate
Some failures should not trigger another model call. The correct fallback is a controlled stop.
Case 1: partial streaming output
Once response tokens have reached the user, silently switching models can create contradictions, duplicate content, broken code blocks, or a sudden style change. It also makes the final response difficult to attribute and debug.
Use one of these explicit outcomes instead:
- End the stream with a recoverable error and a “retry” action.
- Offer to restart the answer from the beginning.
- Continue only if the application has a designed resume protocol and the new model receives the exact accepted prefix.
The default should be allowAfterPartialOutput: false.
Case 2: uncertain tool side effects
Suppose a model selected a payment, email, deployment, ticket, or database-write tool. The tool may have succeeded even if the connection failed before your orchestrator recorded the result. Replaying the full workflow can duplicate the side effect.
Protect write-side tools with:
- An idempotency key based on the user operation, not the provider attempt.
- A durable execution record with
planned,started,succeeded,failed, andunknownstates. - Deduplication at the tool boundary.
- A reconciliation query before any replay.
- Human review for high-impact actions that remain uncertain.
type ToolExecution = {
operationId: string;
toolName: string;
state: "planned" | "started" | "succeeded" | "failed" | "unknown";
externalReference?: string;
};
function nextAction(execution: ToolExecution): "continue" | "reconcile" | "stop" {
if (execution.state === "succeeded") return "continue";
if (execution.state === "failed") return "stop";
return "reconcile";
}
Keep provider credentials and tool credentials separate. The secure API key management guide covers the surrounding secret and access-control model.
Case 3: safety, permission, or policy uncertainty
Availability should not weaken a safety or authorization decision. If the fallback candidate does not support the required policy controls, the route is ineligible. If the system cannot determine whether an operation is allowed, fail closed or escalate according to the product’s risk model.
Case 4: no candidate satisfies the contract
Return a typed failure that the application can handle:
{
"status": "unavailable",
"reason": "no_eligible_fallback",
"retryable": true,
"retry_after_ms": 30000,
"request_id": "req_123"
}
A clear degraded response is better than a successful-looking answer that violates the schema, uses the wrong tools, or performs the wrong side effect.
Put the three workflows into one state machine
The orchestration layer should make the transition explicit.
START
-> PRIMARY_ATTEMPT
-> SUCCESS: validate and return
-> TRANSIENT + replayable: WORKFLOW_1
-> CONTRACT_FAILURE + approved alternate: WORKFLOW_2
-> PARTIAL_OUTPUT or SIDE_EFFECT_UNCERTAIN: WORKFLOW_3
WORKFLOW_1
-> retry inside budget
-> equivalent failover inside budget
-> if compatible alternate allowed: WORKFLOW_2
-> otherwise: STOP
WORKFLOW_2
-> capability check
-> alternate attempt
-> contract and task validation
-> return only on validated success
-> otherwise: STOP
WORKFLOW_3
-> mark partial or uncertain state
-> reconcile external side effects when possible
-> offer explicit restart or human escalation
-> never silently replay unsafe work
This is also the right boundary for a multi-model gateway. Centralizing model access behind an OpenAI-compatible endpoint can reduce integration duplication, but the application still needs to supply workflow intent: deadlines, side-effect mode, required tools, schema version, and whether cross-model fallback is allowed. Flatkey provides a unified API access layer for teams that want one key and one integration surface across model providers; the safest routing policy still starts with explicit application contracts.
Run five failure drills before enabling automatic fallback
A fallback path that has never handled a controlled failure is only a diagram. Test each route class against failures that exercise a different safety boundary.
| Drill | Injected condition | Expected behavior | Evidence to retain |
|---|---|---|---|
| 1. Primary timeout | Delay the primary beyond its per-attempt timeout | Retry only if the total deadline and attempt budget remain | Attempt timestamps, budget before and after, final route reason |
| 2. Rate-limit burst | Return a bounded series of rate-limit responses | Apply jitter, respect retry guidance, and avoid synchronized retries | Backoff distribution, queue depth, recovered and deadline-exhausted counts |
| 3. Invalid structured output | Return HTTP success with a schema-invalid body | Mark contract failure, try only an approved schema-capable alternate, validate again | Validation errors, candidate eligibility record, accepted-task result |
| 4. Mid-stream disconnect | End the connection after user-visible tokens | Stop the stream and require an explicit restart | Partial-output flag, user-facing state, confirmation that no silent splice occurred |
| 5. Ambiguous tool result | Drop the response after a write-side tool may have executed | Reconcile by operation ID before any replay | Idempotency record, external state lookup, duplicate-side-effect count |
Run the drills first in a local or staging environment, then in a narrowly scoped production game day. The purpose is not to prove that every request survives. It is to prove that the system fails in the intended state, exposes enough evidence to diagnose the event, and does not spend more latency, money, or side-effect risk than the policy allows.
For each drill, verify four layers independently:
- Decision correctness: the router chose the intended workflow.
- Budget correctness: all attempts stayed inside the shared deadline, attempt, and cost envelope.
- Output correctness: the final result passed contract and task validation, or returned an explicit degraded state.
- Audit correctness: traces captured policy version, failure class, candidate eligibility, route reason, and user-visible outcome.
Repeat the drill whenever you change a provider adapter, retry owner, model candidate, schema version, tool contract, or streaming implementation. Those changes can alter replay safety even when the public API shape appears unchanged.
Use a fallback readiness scorecard before production
Passing a few happy-path tests is not enough to enable automatic fallback. A route should earn automation by passing five independent release gates.
| Gate | Pass condition | Evidence | Block automatic fallback when |
|---|---|---|---|
| Replay safety | The team can prove whether the request is safe to repeat at every attempt boundary | Side-effect classification, idempotency design, partial-output rules | A write may have happened without a reconciliation key |
| Contract compatibility | Every candidate supports the required context, tools, schema, modalities, and policy controls | Versioned capability matrix and contract tests | Compatibility is assumed from model family or marketing labels |
| Task quality | The alternate produces acceptable outcomes for the route's real workload | Route-specific evaluation set and reviewed failure cases | Only transport success or generic benchmark scores are available |
| Budget control | Retries and fallbacks share one deadline, attempt limit, and cost ceiling | Failure-drill traces showing budget consumption | Multiple layers can retry independently or exceed the caller deadline |
| Operational control | On-call engineers can identify, disable, and explain a fallback decision | Policy version, route reason, kill switch, dashboard, runbook | The recovery path cannot be isolated without a full application deploy |
Treat the scorecard as a release artifact. Record the route class, policy version, approved candidates, evaluator version, drill results, owner, and review date. A single global “fallback enabled” flag hides too much risk; approval should happen per workflow class.
Copyable readiness record
fallback_readiness:
route_class: support_ticket_extraction
policy_version: fallback-v4
owner: ai-platform
primary_target: primary-model
approved_candidates:
- equivalent-deployment
- alternate-model
gates:
replay_safety: pass
contract_compatibility: pass
task_quality: pass
budget_control: pass
operational_control: pass
evidence:
capability_matrix: contracts/support-ticket-v3.yaml
evaluation_set: evals/support-ticket-2026-08.jsonl
failure_drill_run: drills/2026-08-03.json
dashboard: ai-routing/support-ticket
runbook: runbooks/support-ticket-fallback.md
release:
mode: canary
rollback_owner: oncall-ai-platform
next_review_at: 2026-09-03
The file does not need to live in this exact format. What matters is that the release decision is reviewable and tied to the same policy version recorded in production traces.
Roll out a model fallback strategy in four stages
Automatic fallback should not jump from an offline test to every production request. Use four stages that expose decision errors before they become user-visible.
Stage 1: shadow the decision
Run the fallback controller in observe-only mode. The primary path still determines the user response, while the controller records what it would have done.
Review:
- How often the policy labels a failure as retryable.
- How often a candidate is eligible.
- Which budget would have stopped recovery.
- Whether the policy proposes fallback after partial output or uncertain side effects.
- Whether provider-normalized errors preserve enough detail for incident diagnosis.
Shadow mode is especially useful for finding overly broad rules such as “fallback on every 429” or “try another model after any schema error.” Those rules can look reasonable in code review but behave badly against real request states.
Stage 2: canary low-risk workflows
Enable fallback for a narrow share of replay-safe traffic such as read-only classification, extraction, or background summarization. Exclude write-side tools, safety-sensitive decisions, and routes with user-visible streaming.
Compare the canary against the primary-only path using route-level outcomes:
- Accepted-task rate, not only HTTP success.
- Added latency from recovery.
- Cost delta per accepted task.
- Contract-validation failures by candidate.
- Deadline exhaustion and no-eligible-fallback rate.
- User cancellation or explicit restart rate.
Do not widen the canary because the provider error rate fell. Widen it only when the final user outcome remains acceptable and the recovery path stays inside its envelope.
Stage 3: constrain automatic recovery by risk class
Expand only the workflow classes that passed the readiness scorecard. Keep policy differences explicit:
| Risk class | Default automation | Required safeguard |
|---|---|---|
| Read-only, no streamed output | Retry, equivalent failover, approved cross-model fallback | Contract and task validation |
| Read-only with streamed output | Recovery only before first user-visible byte | Partial-output state and explicit restart |
| Tool use with read-only tools | Retry before tool execution; validate alternate tool contract | Tool schema and tool-choice tests |
| Tool use with writes | Stop and reconcile after ambiguous execution | Durable operation ID and external state lookup |
| Safety, permission, or compliance decision | Fail according to the product's approved policy | No availability-driven policy downgrade |
This stage is where a gateway and application contract meet. The gateway can normalize errors, enforce budgets, and select eligible capacity. The application must still state whether output has escaped, whether a side effect is possible, and which quality or policy checks are mandatory.
Stage 4: widen gradually and re-certify changes
Increase traffic in bounded steps. At each step, retain the ability to disable one policy version, one route class, one provider adapter, or one candidate without turning off the entire routing layer.
Re-run the relevant scorecard gates when any of these change:
- Model or model version.
- Provider adapter or endpoint.
- Prompt template or system instruction.
- Tool definition or permission scope.
- Structured-output schema.
- Retry ownership or timeout configuration.
- Streaming transport or client behavior.
- Safety policy or quality evaluator.
Fallback readiness expires when its assumptions change. A candidate approved for a previous prompt, schema, or tool set should not remain automatically eligible by inertia.
Define rollback triggers before enabling the canary
A canary is only safe when the team agrees in advance what stops it. Use route-specific triggers rather than waiting for a broad incident.
Rollback or disable the affected policy when you observe:
- Duplicate or uncertain write-side effects.
- Cross-model contract success without acceptable task success.
- A rise in partial-stream failures or invisible response splicing.
- Repeated deadline exhaustion caused by recovery attempts.
- Budget ceilings being exceeded or ignored.
- Candidate selection that violates a required capability or safety policy.
- An unexplained change in fallback reason distribution after a deploy.
- Missing policy-version or attempt-level trace data during an incident.
The rollback action should be as narrow as the failure. Depending on the event, that may mean disabling one candidate, forcing a route to equivalent failover only, setting allowCrossModelFallback to false, opening a circuit for one provider, or returning the workflow to primary-only mode.
Avoid a rollback mechanism that requires rebuilding the application. Recovery policy changes frequently during incidents, and the safest response is often a configuration change with an auditable version rather than an emergency code patch.
Use one incident worksheet for every fallback event
Fallback incidents become hard to diagnose when each provider exposes a different error shape and each application logs a different request state. Capture one provider-neutral worksheet.
fallback_incident:
incident_id: inc-2026-08-03-001
route_class: support_ticket_extraction
request_id: req_123
policy_version: fallback-v4
request_state:
output_started: false
side_effect_mode: none
tool_execution_state: not_started
deadline_remaining_ms: 1820
attempts_remaining: 1
primary_failure:
normalized_class: overloaded
provider_status: 529
retry_guidance_present: true
recovery_decision:
workflow: cross_model_fallback
candidate: alternate-model
reason: equivalent_capacity_unavailable
validation:
transport_success: true
contract_success: true
task_success: false
failure_reason: required_field_omitted
user_outcome:
state: explicit_failure
partial_output: false
duplicate_side_effect: false
containment:
action: disable_candidate_for_route
owner: oncall-ai-platform
The most important distinction is between recovery success and user success. A fallback request can return a valid HTTP response and still fail the schema, choose the wrong tool, omit a required fact, or violate the route's quality threshold. Incident review should follow the outcome all the way to the user-visible task.
Run a 60-minute model fallback game day
Unit tests prove that individual branches execute. A fallback game day proves that the whole recovery system behaves correctly while deadlines, retries, streams, validation, tools, telemetry, and operator controls interact.
Run the exercise against one workflow class at a time. Do not begin with a global provider outage simulation. A narrow route such as read-only extraction or internal summarization produces clearer evidence and limits the blast radius if the policy is wrong.
Define the game-day charter
Write a one-page charter before anyone injects a failure. The charter prevents the exercise from turning into an improvised outage.
game_day:
id: fallback-gd-2026-08-04-extraction
route_class: structured_extraction
policy_version: fallback-v4
environment: staging
exercise_owner: ai-platform
incident_commander: reliability
primary_target: primary-model
approved_fallbacks:
- equivalent-deployment
- alternate-schema-capable-model
traffic_scope:
synthetic_requests: 100
production_percentage: 0
safety_limits:
stop_after_minutes: 60
max_error_rate_percent: 5
max_duplicate_side_effects: 0
max_unexplained_route_decisions: 0
success_definition:
- every request ends accepted, explicitly degraded, or safely stopped
- no request exceeds the shared attempt budget
- no partial stream is silently continued by another model
- every fallback decision includes a policy version and route reason
Use synthetic or replay-safe traffic first. If the route can trigger writes, replace the tool with a controlled test double or a sandbox that supports idempotency lookup. A game day should test recovery controls, not gamble with customer state.
Assign four roles
Keep the team small enough to make decisions quickly, but separate observation from execution.
| Role | Responsibility during the exercise | Must not do |
|---|---|---|
| Exercise lead | Starts scenarios, controls the timeline, and calls stop conditions | Change the fallback policy mid-scenario without recording it |
| Operator | Watches route health, disables candidates, and uses the kill switch | Inject failures or edit evidence |
| Observer | Records timestamps, screenshots, traces, and user-visible outcomes | Help the router “pass” by correcting requests manually |
| Application owner | Judges task quality and workflow-specific degradation | Approve a result based only on HTTP success |
For a very small team, one person can cover two roles, but the person injecting the fault should not be the only person evaluating whether the system responded correctly.
Build a scenario ladder
Start with the least ambiguous failure and add risk only after the route passes the previous rung.
| Rung | Injection | What the router should prove | Promotion requirement |
|---|---|---|---|
| 1. Clean equivalent failover | Make the primary endpoint unavailable before response bytes | It can move to equivalent capacity without changing the application contract | Accepted result, one route reason, shared budget respected |
| 2. Retry pressure | Return a bounded burst of retryable errors | Backoff and jitter work without attempt multiplication | No nested retry amplification; deadline remains authoritative |
| 3. Semantic contract failure | Return a transport-successful but invalid structured result | Validation, not status code, controls acceptance | Alternate is eligible and its result passes the same validator |
| 4. Partial stream | Disconnect after visible output | The system stops and marks the answer partial | No silent model splice; restart is explicit |
| 5. Uncertain tool completion | Lose the model response after a write may have executed | The workflow reconciles external state before replay | Operation ID lookup completes; duplicate writes remain zero |
| 6. Fallback degradation | Make the approved alternate slower or lower quality | Stop-loss and rollback rules override availability pressure | Candidate is removed or automation is disabled at the predefined threshold |
Do not skip directly to a complicated cross-model scenario. If equivalent failover cannot preserve the budget and trace contract, adding a behaviorally different model will make the diagnosis harder, not more realistic.
Inject faults at explicit boundaries
Label the exact boundary where the fault enters the request lifecycle. “Provider failed” is too vague for a useful test record.
type InjectionPoint =
| "before_connect"
| "after_connect_before_headers"
| "after_headers_before_body"
| "after_partial_stream"
| "after_tool_dispatch_before_ack"
| "after_tool_ack_before_model_response"
| "after_transport_success_before_validation";
The boundary determines which recovery actions are safe. A timeout before connection can often be retried. A disconnect after a user has seen output requires an explicit restart. A lost acknowledgement after a write-side tool call requires reconciliation. Treating all three as the same timeout class is how duplicate actions and incoherent responses enter production.
If your fault-injection layer cannot target those boundaries, add the boundary marker to the provider adapter or orchestration layer before the exercise. Coarse failure toggles are useful for availability tests but insufficient for replay-safety tests.
Capture one evidence row per request
The game day should produce a request-level ledger, not only dashboard screenshots. A compact row makes unexplained decisions visible.
| Field | Example | Why it matters |
|---|---|---|
request_id |
req_01J... |
Joins gateway, model, validator, and tool evidence |
scenario_id |
partial-stream-01 |
Connects the outcome to the injected condition |
policy_version |
fallback-v4 |
Proves which routing rules made the decision |
failure_class |
stream_interrupted |
Separates transport, contract, policy, and tool uncertainty |
injection_point |
after_partial_stream |
Establishes replay safety |
attempts_used |
1/2 |
Detects retry amplification |
elapsed_ms |
4830/12000 |
Shows remaining deadline budget |
cost_budget_state |
within |
Prevents recovery from ignoring unit economics |
selected_action |
restart_required |
Records the router's decision |
candidate_id |
none |
Shows whether another model was considered |
validator_result |
not_run |
Separates transport recovery from task acceptance |
side_effect_state |
none |
Makes reconciliation requirements explicit |
user_outcome |
partial_marked |
Captures what the customer experienced |
operator_action |
none |
Distinguishes automatic recovery from manual containment |
Store the ledger beside the policy snapshot, validator version, fault configuration, and dashboard export. Without those versions, a passing exercise cannot be reproduced after the next adapter or model change.
Score the exercise with promotion rules
Use three possible decisions: promote, fix and rerun, or stop automation. Avoid a vague “mostly passed” result.
Promote the route only when all of these are true:
- Every request has an explained terminal state.
- No attempt chain exceeds the shared deadline, attempt count, or configured cost ceiling.
- Every accepted output passes the route's validator or evaluation rule.
- Partial output and uncertain side effects enter explicit stop or reconciliation states.
- Operators can disable one candidate or the whole policy without deploying application code.
- Alerting identifies both recovery failures and harmful recoveries, such as fallback success with unacceptable task quality.
Choose fix and rerun when the safety model is correct but evidence or implementation is incomplete. Examples include a missing route reason, an alert that fires too late, or a candidate that passes the contract but misses the latency objective.
Choose stop automation when the exercise finds replay ambiguity, duplicate side effects, silent stream splicing, unexplained routing, policy bypass, or a failure mode the current state machine cannot represent. Those are design gaps, not tuning issues.
Use a copyable game-day scorecard
game_day_result:
game_day_id: fallback-gd-2026-08-04-extraction
route_class: structured_extraction
policy_version: fallback-v4
evaluator_version: extraction-eval-v7
started_at: 2026-08-04T09:00:00Z
completed_at: 2026-08-04T10:00:00Z
scenarios:
equivalent_failover: pass
retry_pressure: pass
semantic_contract_failure: pass
partial_stream: pass
uncertain_tool_completion: not_applicable
fallback_degradation: fix
totals:
requests: 100
accepted: 94
explicitly_degraded: 6
unsafe_or_unexplained: 0
duplicate_side_effects: 0
deadline_violations: 0
decision: fix_and_rerun
blockers:
- alternate p95 latency exceeded the route objective during degradation
owner: ai-platform
rerun_due: 2026-08-11
The sample values are illustrative. Use your own route objectives and evaluation thresholds. What matters is that the final decision points to retained evidence and a named owner.
Turn findings into release controls
Finish the game day by converting every finding into one of four durable controls:
- Policy change: candidate eligibility, attempt budget, deadline, or route-class rule.
- Contract test: capability, schema, tool, streaming, or safety compatibility check.
- Operational control: alert, dashboard, kill switch, candidate quarantine, or incident procedure.
- Product behavior: explicit restart, degraded-state message, manual confirmation, or reconciliation screen.
Do not close the exercise with a list of observations. A finding without an owner, control type, and rerun condition will reappear during a real incident.
For the telemetry layer behind these exercises, use the LLM API observability guide. For retry ownership and rate-limit behavior, pair the game day with the LLM rate limits and retry strategy. If your team is still defining the gateway boundary, start with the LLM gateway beginner guide.
A seven-day implementation sequence
Teams can use this order to move from an ad hoc model list to a controlled workflow playbook:
- Day 1 — Inventory routes: classify output mode, side-effect risk, tools, schemas, deadlines, and current retry owners.
- Day 2 — Define envelopes: set attempt, latency, cost, capability, and replay limits per route class.
- Day 3 — Build contracts: document approved candidates and test tool, schema, context, modality, and policy compatibility.
- Day 4 — Instrument decisions: record normalized failure, request state, policy version, candidate eligibility, budget, validation, and user outcome.
- Day 5 — Run failure drills: inject timeout, rate-limit burst, invalid output, mid-stream disconnect, and ambiguous tool execution.
- Day 6 — Shadow and canary: observe decisions first, then enable a narrow low-risk route with predefined rollback triggers.
- Day 7 — Review and widen: inspect accepted-task rate, added latency, cost delta, unsafe replay signals, and no-eligible-fallback events before expanding.
The sequence is intentionally workflow-first. Choosing a ranked list of models is one small step. The production work is proving when the system may continue, when it must validate, and when it must stop.
Model fallback strategy rollout checklist
Policy
- Every route class has a fallback envelope.
- Fallback policy is versioned and reviewable as configuration.
- Retryable errors are normalized across providers.
- The total retry budget has one owner.
- Equivalent endpoints are distinguished from alternate models.
- Cross-model candidates have versioned capability contracts.
- Partial output disables transparent fallback by default.
- Write-side tools use durable idempotency records.
Validation
- Transport, contract, and task success are measured separately.
- Structured outputs are validated after fallback.
- Tool arguments and tool-choice behavior are tested per model.
- Fallback evaluation sets represent real route classes.
- New candidates pass offline evaluation and a production canary.
- All five failure drills pass for each applicable route class.
Operations
- Each attempt records route reason, target, latency, and result.
- Dashboards show primary, retry, equivalent failover, and cross-model recovery separately.
- Alerts include deadline exhaustion and no-eligible-fallback rates.
- Circuit breakers use controlled half-open probes.
- Incident review includes user-visible quality and duplicate-side-effect risk.
- Every attempt trace records the active fallback policy version.
- Each route has a completed readiness scorecard and named owner.
- Canary rollback triggers and narrow kill switches are tested.
- Incident worksheets capture request state, validation, and user outcome.
Metrics that prove fallback is helping
Do not optimize only for provider error rate. Track the user outcome.
| Metric | Question answered |
|---|---|
| Retry recovery rate | Are same-target retries worth their latency? |
| Equivalent failover recovery rate | Does redundant capacity restore service safely? |
| Cross-model contract success | Does the alternate response satisfy the required interface? |
| Cross-model task success | Does the user still complete the intended job? |
| Added fallback latency | How much delay does recovery add? |
| Fallback cost delta | What is the cost of the recovery path? |
| Partial-stream failure rate | How often does the system reach an unrecoverable presentation state? |
| Side-effect reconciliation rate | How often must the system verify external state before continuing? |
| Duplicate-side-effect incidents | Did replay protection fail? |
| No-eligible-fallback rate | Are route contracts too strict, or is capacity insufficient? |
Segment these metrics by route class. An aggregate recovery rate can hide that fallback works well for extraction but poorly for code generation or tool use.
During rollout, compare these metrics by policy version and release stage. That makes it possible to separate a provider incident from a controller change, candidate change, or widened canary.
Frequently asked questions
What is a model fallback strategy?
A model fallback strategy is a policy for deciding when an AI request should retry the same target, fail over to equivalent capacity, switch to an approved alternate model, or stop because replay would be unsafe.
What is the difference between retry and fallback?
A retry repeats the request against the same target or deployment. Equivalent failover moves the request to capacity intended to preserve the same model contract. Cross-model fallback changes the model and therefore requires capability and quality validation.
Should every 429 error trigger another model?
No. First classify the limit, honor retry guidance, check the remaining deadline, and use a bounded retry or queue. Switching models may help when approved alternate capacity exists, but it can also change output quality, tool behavior, or cost.
Can a streamed response fall back mid-answer?
It is usually safer not to switch transparently after tokens have reached the user. Stop the stream and offer an explicit restart unless the application has a tested resume protocol.
How many fallback models should a route have?
Use the smallest approved set that provides meaningful recovery. Each candidate adds evaluation, monitoring, and incident-response work. A long untested list is not resilience.
Where should fallback logic live?
Centralize provider normalization, routing, attempt budgets, and observability in a gateway or orchestration layer. Keep workflow-specific intent—side-effect risk, schema requirements, safety policy, and quality thresholds—close to the application.
How should a team roll out automatic model fallback?
Start in shadow mode, canary only replay-safe workflows, define rollback triggers before widening traffic, and re-certify the fallback policy whenever models, prompts, tools, schemas, retry ownership, or safety requirements change.
Build fallback around workflow risk
The best model fallback strategy is not “try the next model.” It is a bounded decision system:
- Workflow 1 recovers replayable requests with retries and equivalent capacity.
- Workflow 2 switches models only after capability and quality checks.
- Workflow 3 stops automatic replay when output or side effects make recovery unsafe.
That design improves availability without hiding contract failures or duplicating user actions. If your team is standardizing access across model providers, use Flatkey’s unified OpenAI-compatible API layer as the integration surface, then attach these workflow-specific envelopes to every production route.



