From fadbcba32d0d7a41b16caf0694c48d879a1af231 Mon Sep 17 00:00:00 2001 From: michaelxer Date: Mon, 15 Jun 2026 07:06:02 +0700 Subject: [PATCH] fix: guard check_and_fix_namespace against None key When user_id is None, the cache key can be None, causing AttributeError: 'NoneType' object has no attribute 'startswith' in check_and_fix_namespace. Add an early return for None key to prevent the error and the ERROR-level log noise it produces on every unauthenticated request. Fixes #30424 --- litellm/caching/redis_cache.py | 2 + ...test_check_and_fix_namespace_none_guard.py | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_litellm/caching/test_check_and_fix_namespace_none_guard.py diff --git a/litellm/caching/redis_cache.py b/litellm/caching/redis_cache.py index 7239bea7853..83dfddfba1a 100644 --- a/litellm/caching/redis_cache.py +++ b/litellm/caching/redis_cache.py @@ -369,6 +369,8 @@ class RedisCache(BaseCache): """ Make sure each key starts with the given namespace """ + if key is None: + return key if self.namespace is not None and not key.startswith(self.namespace): key = self.namespace + ":" + key diff --git a/tests/test_litellm/caching/test_check_and_fix_namespace_none_guard.py b/tests/test_litellm/caching/test_check_and_fix_namespace_none_guard.py new file mode 100644 index 00000000000..c049c3157f4 --- /dev/null +++ b/tests/test_litellm/caching/test_check_and_fix_namespace_none_guard.py @@ -0,0 +1,49 @@ +""" +Test that check_and_fix_namespace handles None key gracefully. + +Regression test for https://github.com/BerriAI/litellm/issues/30424 +""" +from unittest.mock import MagicMock + +from litellm.caching.redis_cache import RedisCache + + +def test_check_and_fix_namespace_with_none_key(): + """When key is None, check_and_fix_namespace should return None without raising.""" + cache = MagicMock(spec=RedisCache) + cache.namespace = "litellm" + # Call the real method + result = RedisCache.check_and_fix_namespace(cache, key=None) + assert result is None + + +def test_check_and_fix_namespace_with_none_key_no_namespace(): + """When key is None and namespace is None, should return None without raising.""" + cache = MagicMock(spec=RedisCache) + cache.namespace = None + result = RedisCache.check_and_fix_namespace(cache, key=None) + assert result is None + + +def test_check_and_fix_namespace_with_valid_key(): + """Normal behavior: prefix key with namespace if not already prefixed.""" + cache = MagicMock(spec=RedisCache) + cache.namespace = "litellm" + result = RedisCache.check_and_fix_namespace(cache, key="my_key") + assert result == "litellm:my_key" + + +def test_check_and_fix_namespace_with_already_prefixed_key(): + """If key already starts with namespace, don't double-prefix.""" + cache = MagicMock(spec=RedisCache) + cache.namespace = "litellm" + result = RedisCache.check_and_fix_namespace(cache, key="litellm:my_key") + assert result == "litellm:my_key" + + +def test_check_and_fix_namespace_no_namespace(): + """When namespace is None, return key as-is.""" + cache = MagicMock(spec=RedisCache) + cache.namespace = None + result = RedisCache.check_and_fix_namespace(cache, key="my_key") + assert result == "my_key"