{"record":{"id":"92d772f02c0f3c80","repo":"sgl-project/sglang","slug":"radixkey-operations-require-matching-cache-salt-b","errorCode":null,"errorMessage":"RadixKey operations require matching cache_salt, but got {self.cache_salt=} != {other.cache_salt=}","messagePattern":"RadixKey operations require matching cache_salt, but got (.+?) != (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/mem_cache/radix_cache.py","lineNumber":176,"sourceCode":"        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\n        # holding the divergence -- no per-token Python loop on long shared prefixes.\n        matched_tokens = n\n        lo = 0\n        step = 1\n        while lo < n:","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/mem_cache/radix_cache.py#L158-L194","documentation":"RadixKey binary operations require identical cache_salt on both keys. cache_salt namespaces the prefix cache (e.g. per-tenant or per-session salt) so entries from different salts never mix; _check_compatible raises ValueError when they differ.","triggerScenarios":"Calling key_a.match(key_b) (directly or via match_prefix) where one key was created with cache_salt='tenant-a' and the other with a different or None salt.","commonSituations":"Enabling cache_salt on some requests but constructing probe keys without the salt; changing the salt between insert and lookup; multi-tenant deployments where the salt is dropped in a helper function.","solutions":["Thread the request's cache_salt through every RadixKey construction on both insert and lookup paths","Set a consistent global salt (or per-tenant salt) instead of mixing salted and unsalted keys","Assert key.cache_salt equality in tests to catch drift early"],"exampleFix":"# before\nprobe = RadixKey(token_ids, extra_key=extra)  # salt lost\n# after\nprobe = RadixKey(token_ids, extra_key=extra, cache_salt=req.cache_salt)","handlingStrategy":"validation","validationCode":"if key_a.cache_salt != key_b.cache_salt:\n    raise ValueError(\"cache_salt mismatch\")","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":["Thread cache_salt through every key construction site","Add tests asserting insert/lookup keys share salt"],"tags":["radix-cache","cache-salt","multi-tenant","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"}