From dc6611b4f7b362094ee573d09e9581c8ff2d168c Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:14:26 +0000 Subject: [PATCH] fix(router): forward include_fallback_errors through multi-hop fallbacks run_async_fallback received include_fallback_errors as an explicit named parameter, so it was bound out of **kwargs and never reached the nested async_function_with_fallbacks call. Multi-hop fallback chains (a fallback group that itself fails over) therefore stopped collecting fallback errors beyond the first hop when a caller opted in. Re-inject the flag into kwargs before the nested call so inner hops keep accumulating errors, which add_fallback_headers_to_response already merges across levels. --- .../router_utils/fallback_event_handlers.py | 2 + .../test_fallback_event_handlers.py | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/litellm/router_utils/fallback_event_handlers.py b/litellm/router_utils/fallback_event_handlers.py index 3635a403105..f0edc7fc9db 100644 --- a/litellm/router_utils/fallback_event_handlers.py +++ b/litellm/router_utils/fallback_event_handlers.py @@ -139,6 +139,8 @@ async def run_async_fallback( fallback_depth = fallback_depth + 1 kwargs["fallback_depth"] = fallback_depth kwargs["max_fallbacks"] = max_fallbacks + if include_fallback_errors: + kwargs["include_fallback_errors"] = include_fallback_errors response = await litellm_router.async_function_with_fallbacks( *args, **kwargs ) diff --git a/tests/test_litellm/router_utils/test_fallback_event_handlers.py b/tests/test_litellm/router_utils/test_fallback_event_handlers.py index 1eeeba7b7b0..ca647bdce55 100644 --- a/tests/test_litellm/router_utils/test_fallback_event_handlers.py +++ b/tests/test_litellm/router_utils/test_fallback_event_handlers.py @@ -80,6 +80,51 @@ async def test_run_async_fallback_raises_when_all_fallbacks_fail(): ) +class RecordingRouter: + def __init__(self): + self.received_kwargs = None + + def log_retry(self, kwargs, e): + return kwargs + + async def async_function_with_fallbacks(self, *args, **kwargs): + self.received_kwargs = kwargs + return StreamingWrapper() + + +@pytest.mark.asyncio +async def test_run_async_fallback_forwards_include_fallback_errors_to_nested_call(): + """A nested fallback (multi-hop) must keep collecting errors, so the opt-in + flag has to reach the nested async_function_with_fallbacks call.""" + router = RecordingRouter() + await run_async_fallback( + litellm_router=router, + fallback_model_group=["fallback-model"], + original_model_group="primary-model", + original_exception=RuntimeError("upstream limited request"), + max_fallbacks=3, + fallback_depth=0, + include_fallback_errors=True, + ) + + assert router.received_kwargs.get("include_fallback_errors") is True + + +@pytest.mark.asyncio +async def test_run_async_fallback_does_not_forward_flag_without_opt_in(): + router = RecordingRouter() + await run_async_fallback( + litellm_router=router, + fallback_model_group=["fallback-model"], + original_model_group="primary-model", + original_exception=RuntimeError("upstream limited request"), + max_fallbacks=3, + fallback_depth=0, + ) + + assert "include_fallback_errors" not in router.received_kwargs + + @pytest.mark.asyncio async def test_run_async_fallback_skips_original_model_group(): response = await run_async_fallback(