fix: deep copy kwargs in run_async_fallback to prevent mutation

The router reuses the same kwargs dict across fallback attempts.
Provider-specific transformations (e.g., Bedrock converse_handler.py
popping 'tools' from optional_params) mutate kwargs in-place,
corrupting them for subsequent fallback providers.

This adds safe_deep_copy(kwargs) before each fallback attempt,
following the same pattern used in fallback_utils.py.

Fixes: https://github.com/BerriAI/litellm/issues/24764
This commit is contained in:
Karlla Nascimento 2026-03-29 16:56:08 -03:00 committed by GitHub
parent 58120537af
commit 0d4934d7e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -8,6 +8,7 @@ from litellm.router_utils.add_retry_fallback_headers import (
add_fallback_headers_to_response,
)
from litellm.types.router import LiteLLMParamsTypedDict
from litellm.litellm_core_utils.core_helpers import safe_deep_copy
if TYPE_CHECKING:
from litellm.router import Router as _Router
@ -123,7 +124,13 @@ async def run_async_fallback(
if mg == original_model_group:
continue
try:
# LOGGING
# Deep copy kwargs to prevent mutations from one provider
# (e.g., Bedrock popping 'tools' from optional_params)
# from corrupting kwargs for subsequent fallback providers.
# See: https://github.com/BerriAI/litellm/issues/24764
kwargs = safe_deep_copy(kwargs)
# LOGGING
kwargs = litellm_router.log_retry(kwargs=kwargs, e=original_exception)
verbose_router_logger.info(f"Falling back to model_group = {mg}")
if isinstance(mg, str):