{"record":{"id":"fbdf165fa26200ce","repo":"sgl-project/sglang","slug":"evictable-size-is-not-implemented-use-full-evic","errorCode":null,"errorMessage":"evictable_size() is not implemented; use full_evictable_size() and swa_evictable_size() instead","messagePattern":"evictable_size\\(\\) is not implemented; use full_evictable_size\\(\\) and swa_evictable_size\\(\\) instead","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/mem_cache/swa_radix_cache.py","lineNumber":855,"sourceCode":"                    self.token_to_kv_pool_allocator.free_swa(node.value)\n                    self.swa_lru_list.remove_node(node)\n                    node.swa_tombstone = True\n                else:\n                    # Internal: standard protected -> evictable.\n                    self.swa_evictable_size_ += len(node.value)\n            node.swa_lock_ref -= 1\n\n            if swa_uuid_for_lock and node.swa_uuid == swa_uuid_for_lock:\n                break\n            node = node.parent\n\n    def sanity_check(self):\n        self.full_lru_list.sanity_check(self)\n        self.swa_lru_list.sanity_check(self)\n\n    def evictable_size(self) -> Tuple[int, int]:\n        # Note: use full_evictable_size() and swa_evictable_size() instead.\n        raise NotImplementedError\n\n    def full_evictable_size(self) -> int:\n        return self.full_evictable_size_\n\n    def swa_evictable_size(self) -> int:\n        return self.swa_evictable_size_\n\n    def protected_size(self) -> Tuple[int, int]:\n        # Note: use full_protected_size() and swa_protected_size() instead.\n        raise NotImplementedError\n\n    def full_protected_size(self) -> int:\n        # protected size refers to the size of the full cache that is locked\n        return self.full_protected_size_\n\n    def swa_protected_size(self) -> int:\n        # protected size refers to the size of the swa cache that is locked\n        return self.swa_protected_size_","sourceCodeStart":837,"sourceCodeEnd":873,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/mem_cache/swa_radix_cache.py#L837-L873","documentation":"SWARadixCache maintains two separate LRU domains (full and SWA), so a single evictable_size() is ambiguous and intentionally not implemented. Callers must use full_evictable_size() and swa_evictable_size().","triggerScenarios":"Calling evictable_size() on a SWARadixCache — code written against the BasePrefixCache interface that assumes a single evictable size.","commonSituations":"Generic cache-monitoring/metrics code that works for non-SWA RadixCache and is reused with SWA enabled models.","solutions":["Replace calls with full_evictable_size() and/or swa_evictable_size()","When writing cache-agnostic code, getattr-guard for the split accessors"],"exampleFix":"# before\nsize = tree.evictable_size()\n# after\nfull = tree.full_evictable_size()\nswa = tree.swa_evictable_size()","handlingStrategy":"validation","validationCode":"assert not hasattr(type(tree), 'swa_evictable_size') or False  # SWA cache detected\nif hasattr(tree, 'swa_evictable_size'):\n    full, swa = tree.full_evictable_size(), tree.swa_evictable_size()","typeGuard":"def is_swa_cache(tree) -> bool:\n    return hasattr(tree, 'full_evictable_size') and hasattr(tree, 'swa_evictable_size')","tryCatchPattern":null,"preventionTips":["Use the split accessors in cache-agnostic code via hasattr checks","Cover SWA and non-SWA paths in metrics unit tests"],"tags":["swa","radix-cache","not-implemented","api-misuse"],"backgroundTag":"method-not-implemented","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}