From 6b755cfce20a64065b4a23d2401574feba839190 Mon Sep 17 00:00:00 2001 From: Abdullah Habib Biswas Date: Sun, 1 Feb 2026 04:46:07 +0530 Subject: [PATCH] fix: prevent error when max_fallbacks exceeds available models (#20071) --- .../router_utils/fallback_event_handlers.py | 12 +++++- tests/test_fallbacks.py | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/litellm/router_utils/fallback_event_handlers.py b/litellm/router_utils/fallback_event_handlers.py index 62e706a0cf5..738b82d7023 100644 --- a/litellm/router_utils/fallback_event_handlers.py +++ b/litellm/router_utils/fallback_event_handlers.py @@ -113,8 +113,16 @@ async def run_async_fallback( The most recent exception if all fallback model groups fail. """ - ### BASE CASE ### MAX FALLBACK DEPTH REACHED - if fallback_depth >= max_fallbacks: + ### BASE CASE ### MAX FALLBACK DEPTH REACHED + if fallback_depth >= max_fallbacks: + raise original_exception + + ### CHECK IF MODEL GROUP LIST EXHAUSTED + if original_model_group in fallback_model_group: + fallback_group_length = len(fallback_model_group) - 1 + else: + fallback_group_length = len(fallback_model_group) + if fallback_depth >= fallback_group_length: raise original_exception error_from_fallbacks = original_exception diff --git a/tests/test_fallbacks.py b/tests/test_fallbacks.py index bc9aa4c64c8..c22cefa6be6 100644 --- a/tests/test_fallbacks.py +++ b/tests/test_fallbacks.py @@ -336,3 +336,45 @@ async def test_chat_completion_bad_and_good_model(): f"Iteration {iteration + 1}: {'✓' if success else '✗'} ({time.time() - start_time:.2f}s)" ) assert success, "Not all good model requests succeeded" + + +@pytest.mark.asyncio +async def test_router_fallback_exhaustion(): + """ + Test for Bug 19985: + """ + from litellm import Router + import pytest + + # Setup: Only ONE fallback model available + model_list = [ + { + "model_name": "gpt-3.5-turbo", + "litellm_params": {"model": "openai/fake", "api_key": "bad-key"}, + }, + { + "model_name": "bad-model-1", + "litellm_params": {"model": "azure/fake", "api_key": "bad-key"}, + } + ] + + # max_fallbacks=10 is much larger than the 1 fallback provided in the list + router = Router( + model_list=model_list, + fallbacks=[{"gpt-3.5-turbo": ["bad-model-1"]}], + max_fallbacks=10 + ) + + try: + # This will fail and attempt to fallback + await router.acompletion( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "test"}] + ) + except Exception as e: + # The success criteria is that we DON'T get an IndexError + assert not isinstance(e, IndexError), f"Expected API error, but got IndexError: {e}" + # Also ensure we actually hit a fallback attempt + print(f"Caught expected exception: {type(e).__name__}") + +