From 21af87fad9d6fea4b4fa8641c32856575dda4791 Mon Sep 17 00:00:00 2001 From: tsushanth Date: Mon, 17 Aug 2026 23:01:22 -0700 Subject: [PATCH] fix(caching): guard disconnect() against None async_redis_conn_pool in cluster mode --- litellm/caching/redis_cache.py | 2 ++ litellm/caching/redis_cluster_cache.py | 10 ++++++++++ tests/test_litellm/caching/test_redis_cache.py | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/litellm/caching/redis_cache.py b/litellm/caching/redis_cache.py index 934ba500ef9..c2958edb482 100644 --- a/litellm/caching/redis_cache.py +++ b/litellm/caching/redis_cache.py @@ -1367,6 +1367,8 @@ class RedisCache(BaseCache): self.redis_client.flushall() async def disconnect(self): + if self.async_redis_conn_pool is None: + return await self.async_redis_conn_pool.disconnect(inuse_connections=True) try: self.redis_client.close() diff --git a/litellm/caching/redis_cluster_cache.py b/litellm/caching/redis_cluster_cache.py index b6dd8047fd4..bb2730a547f 100644 --- a/litellm/caching/redis_cluster_cache.py +++ b/litellm/caching/redis_cluster_cache.py @@ -56,6 +56,16 @@ class RedisClusterCache(RedisCache): async_redis_cluster_client: Final = self.init_async_client() return await async_redis_cluster_client.mget_nonatomic(keys=keys) + async def disconnect(self): + if self.redis_async_redis_cluster_client is not None: + await self.redis_async_redis_cluster_client.aclose() + try: + self.redis_client.close() + except Exception as e: + from litellm._logging import verbose_logger + + verbose_logger.debug("Error closing sync Redis Cluster client: %s", e) + async def test_connection(self) -> dict: """ Test the Redis Cluster connection. diff --git a/tests/test_litellm/caching/test_redis_cache.py b/tests/test_litellm/caching/test_redis_cache.py index 59200719197..6f5da835302 100644 --- a/tests/test_litellm/caching/test_redis_cache.py +++ b/tests/test_litellm/caching/test_redis_cache.py @@ -444,6 +444,17 @@ def test_delete_cache_namespaces_key(namespace, expected, monkeypatch, redis_no_ redis_cache.delete_cache(key="k") mock_client.delete.assert_called_once_with(expected) +@pytest.mark.asyncio +async def test_disconnect_with_none_conn_pool(): + """Regression test: disconnect() must not raise AttributeError when async_redis_conn_pool is None (cluster mode).""" + with patch("asyncio.get_running_loop", side_effect=RuntimeError): + cache = RedisCache(host="localhost", port=6379, password="x") + + cache.async_redis_conn_pool = None + + # Should return without raising + await cache.disconnect() + def _closed_port() -> int: """A port with nothing listening, so Redis calls fail fast and deterministically."""