{"record":{"id":"2a46dae3b213076c","repo":"redis/redis-py","slug":"evictions-count-is-above-cache-size","errorCode":null,"errorMessage":"Evictions count is above cache size","messagePattern":"Evictions count is above cache size","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"redis/cache.py","lineNumber":329,"sourceCode":"        return self._cache\n\n    @cache.setter\n    def cache(self, cache: CacheInterface):\n        self._cache = cache\n\n    @property\n    def type(self) -> EvictionPolicyType:\n        return EvictionPolicyType.time_based\n\n    def evict_next(self) -> CacheKey:\n        self._assert_cache()\n        popped_entry = self._cache.collection.popitem(last=False)\n        return popped_entry[0]\n\n    def evict_many(self, count: int) -> List[CacheKey]:\n        self._assert_cache()\n        if count > len(self._cache.collection):\n            raise ValueError(\"Evictions count is above cache size\")\n\n        popped_keys = []\n\n        for _ in range(count):\n            popped_entry = self._cache.collection.popitem(last=False)\n            popped_keys.append(popped_entry[0])\n\n        return popped_keys\n\n    def touch(self, cache_key: CacheKey) -> None:\n        self._assert_cache()\n\n        if self._cache.collection.get(cache_key) is None:\n            raise ValueError(\"Given entry does not belong to the cache\")\n\n        self._cache.collection.move_to_end(cache_key)\n\n    def _assert_cache(self):","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/cache.py#L311-L347","documentation":"Raised as ValueError in LRUPolicy.evict_many when the requested count exceeds the number of entries currently in the cache collection. evict_many pops count items via popitem; asking for more than exist would underflow the ordered dict, so it validates up front.","triggerScenarios":"Calling eviction_policy.evict_many(n) with n greater than len(cache.collection). This is an internal client-side-caching (CSC) path; evict_many is invoked by cache management code when evicting batches.","commonSituations":"A bug in cache management computing an eviction count from a stale size. Calling evict_many right after a flush or on a nearly-empty cache. Misuse of the eviction policy API directly with an oversized count.","solutions":["Clamp the count to the current cache size before calling evict_many: count = min(count, cache.size).","Use evict_next() in a loop if you need best-effort eviction up to a limit.","Re-check cache.size immediately before eviction to avoid stale counts."],"exampleFix":"# before\nkeys = policy.evict_many(requested_count)  # may exceed size\n# after\nkeys = policy.evict_many(min(requested_count, cache.size))","handlingStrategy":"validation","validationCode":"count = min(count, cache.size)\nkeys = policy.evict_many(count)","typeGuard":null,"tryCatchPattern":"try:\n    keys = policy.evict_many(count)\nexcept ValueError as e:\n    if \"above cache size\" in str(e):\n        keys = policy.evict_many(cache.size)\n    else:\n        raise","preventionTips":["Always clamp eviction counts to the live cache size.","Re-read size immediately before batch eviction."],"tags":["cache","eviction","client-side-caching","validation"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}