diff --git a/litellm/router.py b/litellm/router.py index 2bb725b0880..d6d9f20085f 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -12578,15 +12578,11 @@ class Router: return None return f"claude_code_session_router:v1:{caller_scope}:{session_id}" - async def _delete_claude_code_session_router_binding(self, cache_key: str) -> None: + async def _clear_claude_code_session_router(self, cache_key: str) -> None: try: await self.cache.async_delete_cache(key=cache_key) except Exception as e: # noqa: BLE001 # cache cleanup must not fail an otherwise routable request - verbose_router_logger.warning( - "Failed to delete Claude Code session router binding; " - "the binding may remain until its TTL expires: %s", - e, - ) + verbose_router_logger.debug("Claude Code session router cleanup skipped for %s: %s", cache_key, e) async def _resolve_claude_code_session_router( self, @@ -12605,7 +12601,7 @@ class Router: return registered_model_name bound_registered_model: Final = self._get_model_from_alias(model=bound_model) or bound_model if self._select_pre_routing_strategy(bound_registered_model, request_kwargs) is None: - await self._delete_claude_code_session_router_binding(cache_key) + await self._clear_claude_code_session_router(cache_key) return registered_model_name await self.cache.async_set_cache( key=cache_key, @@ -12620,7 +12616,7 @@ class Router: if request_kwargs.get("fallback_depth") not in (None, 0): return registered_model_name if self._select_pre_routing_strategy(registered_model_name, request_kwargs) is None: - await self._delete_claude_code_session_router_binding(cache_key) + await self._clear_claude_code_session_router(cache_key) return registered_model_name await self.cache.async_set_cache( key=cache_key, diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index 77b73a2d12a..b66dbf6aa7d 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -8428,6 +8428,24 @@ class TestClaudeCodeSubagentSessionRouterBinding: assert response is None redis_cache.async_delete_cache.assert_awaited_once() + @pytest.mark.asyncio + async def test_main_direct_model_still_served_when_cache_delete_fails(self): + router = self._router() + await router.acompletion( + model="smart-router", messages=[{"role": "user", "content": "main turn"}], **self._request_kwargs() + ) + + async def failing_delete(key: str) -> None: + raise Exception("Redis circuit breaker is open — skipping async_delete_cache") + + router.cache.async_delete_cache = failing_delete + + response = await router.acompletion( + model="expensive-model", messages=[{"role": "user", "content": "direct turn"}], **self._request_kwargs() + ) + + assert response.choices[0].message.content == "expensive response" + @pytest.mark.asyncio async def test_background_and_fallback_requests_do_not_clear_the_session_router(self): router = self._router()