{"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/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/cache.py#L311-L347","documentation":"Raised as ValueError by LRUPolicy.evict_many (redis/cache.py:329) when the requested count exceeds the number of entries currently in the cache collection. The LRU policy cannot evict more items than exist; this is a caller invariant violation rather than a cache miss.","triggerScenarios":"Calling evict_many(n) with n greater than the live entry count; computing an eviction count from an external metric (e.g. evictions counter) that overshoots the actual collection size.","commonSituations":"Bug in a custom eviction trigger; off-by-one when evicting a batch; race where entries were removed between sizing and evicting.","solutions":["Cap the requested count at len(cache.collection) before calling evict_many.","Call evict_next() in a loop bounded by min(count, size) instead.","Re-check the collection size immediately before eviction to avoid races."],"exampleFix":"# before\nkeys = policy.evict_many(requested_count)  # ValueError if requested_count > size\n# after\nsize = len(cache.collection)\nkeys = policy.evict_many(min(requested_count, size))","handlingStrategy":"validation","validationCode":"n = min(requested_count, len(cache.collection))\nif n > 0:\n    policy.evict_many(n)","typeGuard":"def eviction_count_ok(policy, n) -> bool:\n    return 0 <= n <= len(policy.cache.collection)","tryCatchPattern":"try:\n    policy.evict_many(n)\nexcept ValueError:\n    policy.evict_many(len(cache.collection))","preventionTips":["Always clamp eviction counts to current size.","Prefer evict_next() in a bounded loop."],"tags":["cache","validation"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}