sgl-project/sglang · error · ValueError

RadixKey operations require matching extra_key, but got {sel

Error message

RadixKey operations require matching extra_key, but got {self.extra_key=} != {other.extra_key=}

What it means

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.

Source

Thrown at python/sglang/srt/mem_cache/radix_cache.py:171

        aligned_len = len(self) // page_size * page_size
        return self[:aligned_len]

    def maybe_to_bigram_view(
        self,
        is_eagle: bool,
        value: Optional[torch.Tensor] = None,
    ) -> Tuple[RadixKey, Optional[torch.Tensor]]:
        # O(1): flip the bigram flag instead of materializing a tuple list.
        # value is paired with raw tokens and gets truncated to the bigram count.
        if is_eagle and not self.is_bigram:
            self.is_bigram = True
            if value is not None:
                value = value[: len(self)]
        return self, value

    def _check_compatible(self, other: RadixKey) -> None:
        if self.extra_key != other.extra_key:
            raise ValueError(
                f"RadixKey operations require matching extra_key, but got "
                f"{self.extra_key=} != {other.extra_key=}"
            )
        if self.cache_salt != other.cache_salt:
            raise ValueError(
                f"RadixKey operations require matching cache_salt, but got "
                f"{self.cache_salt=} != {other.cache_salt=}"
            )

    def match(self, other: RadixKey, page_size: int = 1) -> int:
        """Logical-unit prefix length shared with ``other``. Result is rounded down to ``page_size``."""
        self._check_compatible(other)
        t0, t1 = self.token_ids, other.token_ids
        assert type(t0) is type(t1), (type(t0), type(t1))
        n = min(len(t0), len(t1))

        # Exponential search for the first diverging token: gallop in doubling
        # windows (one C-level slice compare each), then binary-search the window

View on GitHub (pinned to 0132848349)

Solutions

  1. Build both keys from the same RadixKey.make()/constructor call so extra_key is propagated identically
  2. Pass the request's extra_key (LoRA id, encoder id) whenever constructing lookup keys, not just insert keys
  3. Compare extra_key explicitly before calling match if keys come from different sources

Example fix

# before
probe = RadixKey(token_ids)  # extra_key=None
node, val = tree.match_prefix(RadixKey(token_ids, extra_key=req.extra_key))
# after
probe = RadixKey(token_ids, extra_key=req.extra_key)
node, val = tree.match_prefix(probe)
Defensive patterns

Strategy: validation

Validate before calling

if key_a.extra_key != key_b.extra_key:
    raise ValueError("extra_key mismatch; keys belong to different cache namespaces")

Type guard

def keys_compatible(a: RadixKey, b: RadixKey) -> bool:
    return a.extra_key == b.extra_key and a.cache_salt == b.cache_salt

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/3f7fe0ce0077de29. Report an issue: GitHub.