Fallback Chains & Graceful Degradation

9 min read Module 5 of 10 Lesson 14 of 28

What you'll learn

  • Design a three-or-more-tier fallback chain that accounts for quality difference between tiers, not just availability
  • Distinguish a context-window fallback from an availability fallback, and configure both in LiteLLM
  • Implement a graceful-degradation response for when every configured deployment is unavailable
  • Explain why a fallback chain needs to be paired with alerting, not treated as a silent, invisible safety net
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

Lesson 9 configured a basic two-tier fallback; Lesson 13 built the retry, timeout, and circuit-breaker mechanics that decide when a fallback should even engage. This lesson designs the fallback chain itself as a real production artifact: how many tiers, what triggers each transition, and, critically, what “success” even means when a fallback several tiers down answers with real but lower quality than the primary would have.

A Real Multi-Tier Chain

model_list:
  - model_name: production-chat
    litellm_params:
      model: anthropic/claude-sonnet-4-5
      api_key: os.environ/ANTHROPIC_API_KEY

  - model_name: tier2-openai
    litellm_params:
      model: openai/gpt-4o
      api_key: os.environ/OPENAI_API_KEY

  - model_name: tier3-self-hosted
    litellm_params:
      model: hosted_vllm/meta-llama/Llama-3.1-8B-Instruct
      api_base: http://localhost:8000/v1
      api_key: "none"

litellm_settings:
  fallbacks:
    - production-chat: ["tier2-openai", "tier3-self-hosted"]
  context_window_fallbacks:
    - production-chat: ["tier2-openai"]     # separate trigger: input too large for the primary's context limit
  num_retries: 2
  allowed_fails: 3                          # consecutive failures before this deployment is treated as down

Two distinct triggers are configured here, deliberately kept separate. fallbacks handles availability: the primary is down, erroring, or circuit-broken, so try the next tier. context_window_fallbacks handles a structurally different problem: the input is simply too large for the primary model’s context window, which has nothing to do with whether that model is healthy, a long document that needs summarizing should route to a larger-context model immediately, not after exhausting retries against a model that was never going to fit it regardless of health.

Quality-Aware Chain Design

flowchart TD
    T1["Tier 1: claude-sonnet-4-5\nprimary, highest quality"] -->|"unavailable"| T2["Tier 2: gpt-4o\ncomparable quality, different provider"]
    T2 -->|"unavailable"| T3["Tier 3: self-hosted Llama-3.1-8B\nlower quality, but independent\nof any cloud provider's uptime"]
    T3 -->|"unavailable"| Degrade["Graceful degradation:\ncached default / apology response"]

    style T1 fill:#4338CA,stroke:#4338CA,color:#ffffff,rx:8px,ry:8px
    style T2 fill:#EEF0F7,stroke:#6366F1,color:#0F172A,rx:8px,ry:8px
    style T3 fill:#F7F8FC,stroke:#0D9488,color:#0F172A,rx:8px,ry:8px
    style Degrade fill:#F7F8FC,stroke:#94A3B8,color:#0F172A,rx:8px,ry:8px

Notice tier 1 and tier 2 here are chosen to be roughly comparable in quality, both frontier-class models from different providers, so a fallback between them is close to invisible to the end user. Tier 3 is a real quality step down, a much smaller self-hosted model, included anyway because its independence from any cloud provider’s infrastructure makes it worth the quality trade during an outage severe enough to take out two major providers at once. A response that came from tier 3 is a technical success (a 200, a completed answer) but not necessarily an equivalent one, which is why it is worth tagging in the response metadata:

def call_with_tier_tracking(gateway_client, **kwargs):
    response = gateway_client.chat.completions.create(**kwargs)
    # LiteLLM includes which deployment actually served the request in
    # response headers/metadata; surface it so downstream code (and
    # dashboards, Module 8) can distinguish a tier-1 answer from a
    # degraded tier-3 one, rather than treating every 200 as equivalent.
    served_by = response.model  # the real model string that answered, not the alias requested
    if served_by != "claude-sonnet-4-5":
        log_degraded_response(requested="production-chat", served_by=served_by)
    return response

When Every Tier Fails

A chain needs an explicit last resort for the case where every configured deployment, cloud and self-hosted, is unavailable, a genuinely rare but real scenario (a broad network partition, a misconfigured shared credential). Silently returning a 500 to the end user is rarely the best option:

FALLBACK_RESPONSES = {
    "support_chat": "We're experiencing a temporary issue and can't generate a live response right now. Your message has been logged and a team member will follow up.",
}

def handle_total_failure(use_case: str) -> str:
    # A graceful degradation floor: a canned, honest response beats a raw
    # error surfaced to an end user, and it still resolves the request
    # instead of leaving a client hanging on a timeout.
    return FALLBACK_RESPONSES.get(use_case, "This service is temporarily unavailable. Please try again shortly.")

Fallbacks Are Not a Silent Safety Net

The most important operational rule for a fallback chain: every time it engages, it should be observable, ideally alertable, not just logged and forgotten. A team running on tier 3 for six hours because tier 1’s API key silently expired is a real incident that a fully silent fallback chain will hide from everyone until a user complains about answer quality. Lesson 15 covers the complementary mechanism, rate limiting and load shedding, for protecting the gateway (and its own callers) when the problem is not a failing provider but too much legitimate traffic arriving at once.

Knowledge Check

3 questions to test your understanding

1 Why does a well-designed fallback chain need to account for output quality difference between tiers, not just whether each tier is available?

2 What is a context-window fallback, and how does it differ from an availability-triggered fallback?

3 Why should a fallback chain be paired with alerting rather than treated as a fully silent safety net?

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan