{"record":{"id":"3f7fe0ce0077de29","repo":"sgl-project/sglang","slug":"radixkey-operations-require-matching-extra-key-bu","errorCode":null,"errorMessage":"RadixKey operations require matching extra_key, but got {self.extra_key=} != {other.extra_key=}","messagePattern":"RadixKey operations require matching extra_key, but got (.+?) != (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/mem_cache/radix_cache.py","lineNumber":171,"sourceCode":"        aligned_len = len(self) // page_size * page_size\n        return self[:aligned_len]\n\n    def maybe_to_bigram_view(\n        self,\n        is_eagle: bool,\n        value: Optional[torch.Tensor] = None,\n    ) -> Tuple[RadixKey, Optional[torch.Tensor]]:\n        # O(1): flip the bigram flag instead of materializing a tuple list.\n        # value is paired with raw tokens and gets truncated to the bigram count.\n        if is_eagle and not self.is_bigram:\n            self.is_bigram = True\n            if value is not None:\n                value = value[: len(self)]\n        return self, value\n\n    def _check_compatible(self, other: RadixKey) -> None:\n        if self.extra_key != other.extra_key:\n            raise ValueError(\n                f\"RadixKey operations require matching extra_key, but got \"\n                f\"{self.extra_key=} != {other.extra_key=}\"\n            )\n        if self.cache_salt != other.cache_salt:\n            raise ValueError(\n                f\"RadixKey operations require matching cache_salt, but got \"\n                f\"{self.cache_salt=} != {other.cache_salt=}\"\n            )\n\n    def match(self, other: RadixKey, page_size: int = 1) -> int:\n        \"\"\"Logical-unit prefix length shared with ``other``. Result is rounded down to ``page_size``.\"\"\"\n        self._check_compatible(other)\n        t0, t1 = self.token_ids, other.token_ids\n        assert type(t0) is type(t1), (type(t0), type(t1))\n        n = min(len(t0), len(t1))\n\n        # Exponential search for the first diverging token: gallop in doubling\n        # windows (one C-level slice compare each), then binary-search the window","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/mem_cache/radix_cache.py#L153-L189","documentation":"RadixKey binary operations (e.g. match) require both keys to carry identical extra_key metadata, which namespaces cache entries. Mixing keys with different extra_key would compare incomparable cache namespaces, so _check_compatible raises ValueError.","triggerScenarios":"Calling key_a.match(key_b) where key_a.extra_key != key_b.extra_key — e.g. one key built with lora/interrupt/encoder extras and the other without, or with different LoRA IDs.","commonSituations":"Serving multiple LoRA adapters or multimodal encoder configs against one radix cache and reusing a lookup key built without the extra metadata; upgrading from a code path where extra_key was ignored to one where it is enforced.","solutions":["Build both keys from the same RadixKey.make()/constructor call so extra_key is propagated identically","Pass the request's extra_key (LoRA id, encoder id) whenever constructing lookup keys, not just insert keys","Compare extra_key explicitly before calling match if keys come from different sources"],"exampleFix":"# before\nprobe = RadixKey(token_ids)  # extra_key=None\nnode, val = tree.match_prefix(RadixKey(token_ids, extra_key=req.extra_key))\n# after\nprobe = RadixKey(token_ids, extra_key=req.extra_key)\nnode, val = tree.match_prefix(probe)","handlingStrategy":"validation","validationCode":"if key_a.extra_key != key_b.extra_key:\n    raise ValueError(\"extra_key mismatch; keys belong to different cache namespaces\")","typeGuard":"def keys_compatible(a: RadixKey, b: RadixKey) -> bool:\n    return a.extra_key == b.extra_key and a.cache_salt == b.cache_salt","tryCatchPattern":null,"preventionTips":["Always construct both insert and lookup keys with the request's extra_key","Centralize RadixKey construction in one helper so metadata is never dropped"],"tags":["radix-cache","extra-key","lora","value-error","prefix-cache"],"backgroundTag":"cache-key-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}