diff --git a/litellm/router.py b/litellm/router.py index 6da201725b6..6c7611c6236 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -7513,7 +7513,7 @@ class Router: # Check retry policy FIRST, before should_retry_this_error # This allows retry policies to override the healthy deployments check _retry_policy_applies = False - if self.retry_policy is not None or model_group_retry_policy is not None: + if request_num_retries != 0 and (self.retry_policy is not None or model_group_retry_policy is not None): # get num_retries from retry policy # Use the model_group captured at the start of the function, or get it from metadata # kwargs.get("model") at this point is the deployment model, not the model_group diff --git a/tests/test_litellm/test_router_per_deployment_num_retries.py b/tests/test_litellm/test_router_per_deployment_num_retries.py index 1bf5781c2d0..44f0ca319be 100644 --- a/tests/test_litellm/test_router_per_deployment_num_retries.py +++ b/tests/test_litellm/test_router_per_deployment_num_retries.py @@ -490,7 +490,7 @@ class TestRequestNumRetriesBeatsGlobal: litellm.callbacks = prev_callbacks @staticmethod - def _router(global_num_retries): + def _router(global_num_retries, retry_policy=None): return Router( model_list=[ { @@ -503,6 +503,7 @@ class TestRequestNumRetriesBeatsGlobal: } ], num_retries=global_num_retries, + retry_policy=retry_policy, ) async def _count_attempts(self, *, global_num_retries, request_num_retries): @@ -530,6 +531,26 @@ class TestRequestNumRetriesBeatsGlobal: attempts = await self._count_attempts(global_num_retries=3, request_num_retries=0) assert attempts == 1 + @pytest.mark.asyncio + async def test_request_num_retries_zero_disables_retry_policy(self): + """An explicit zero remains a single attempt when a retry policy matches the error.""" + counter = _AttemptCounter() + litellm.callbacks = [counter] + router = self._router( + global_num_retries=3, + retry_policy=RetryPolicy(InternalServerErrorRetries=2), + ) + + with patch("asyncio.sleep", return_value=None): + with pytest.raises(litellm.InternalServerError): + await router.acompletion( + model="mock", + messages=[{"role": "user", "content": "hi"}], + num_retries=0, + ) + + assert counter.attempts == 1 + @pytest.mark.asyncio async def test_global_num_retries_applies_when_request_omits_it(self): """No request num_retries -> the global still applies: 1 initial + 3 retries = 4."""