{"record":{"id":"4b7f43aa8b05ba95","repo":"sgl-project/sglang","slug":"recent-window-tokens-must-be-non-negative-or-none","errorCode":null,"errorMessage":"recent_window_tokens must be non-negative or None","messagePattern":"recent_window_tokens must be non-negative or None","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/layers/kvcache/causal_attention_cache.py","lineNumber":376,"sourceCode":"            [0, sink_end) + [recent_start, updated_local_end)\n\n        Thus ``0`` keeps only sink tokens plus the current chunk.\n        ``cache_head_slice`` applies the same token ranges to a subset of KV\n        heads.\n        \"\"\"\n        if recent_window_tokens is None:\n            if self.global_sink_tokens > 0 or self._has_pinned_sink():\n                return self._pinned_attention_view(\n                    attn_start_index=attn_start_index,\n                    updated_local_end=updated_local_end,\n                    cache_head_slice=cache_head_slice,\n                )\n            return self._cache_slice(\n                slice(attn_start_index, updated_local_end),\n                cache_head_slice=cache_head_slice,\n            )\n        if recent_window_tokens < 0:\n            raise ValueError(\"recent_window_tokens must be non-negative or None\")\n\n        sink_end = min(self._effective_sink_tokens(), updated_local_end)\n        recent_start = max(sink_end, local_start_index - recent_window_tokens)\n        if recent_start <= sink_end:\n            return self._cache_slice(\n                slice(0, updated_local_end),\n                cache_head_slice=cache_head_slice,\n            )\n\n        cache_slices = []\n        if sink_end > 0:\n            cache_slices.append(slice(0, sink_end))\n        if (\n            self._has_pinned_sink()\n            and self.pinned_start >= sink_end\n            and self.pinned_start < recent_start\n        ):\n            cache_slices.append(","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/layers/kvcache/causal_attention_cache.py#L358-L394","documentation":"_visible_attention_kv validates that recent_window_tokens, when not None, is a non-negative integer. Negative values would produce a nonsensical sliding window (recent_start before the sink) and are rejected.","triggerScenarios":"Calling update_and_get_attention_kv (which forwards to _visible_attention_kv) with recent_window_tokens=-1 or any negative int; also triggered by configs mapping a negative window from a CLI/env value.","commonSituations":"A window config parsed as negative due to a sign flip or 'unlimited' sentinel (-1) convention conflicting with this API; passing 0 meaning 'sink only' is fine, negative is not.","solutions":["Pass None for unlimited window or 0 for sink-only","Sanitize config: `recent_window_tokens = max(0, v) if v is not None else None`","Check where the negative value originates (config default, CLI parse)"],"exampleFix":"# before\ncache.update_and_get_attention_kv(k, v, recent_window_tokens=-1)\n# after\ncache.update_and_get_attention_kv(k, v, recent_window_tokens=None)","handlingStrategy":"validation","validationCode":"if recent_window_tokens is not None:\n    recent_window_tokens = max(0, int(recent_window_tokens))","typeGuard":"def is_valid_window(w: int | None) -> bool:\\n    return w is None or (isinstance(w, int) and w >= 0)","tryCatchPattern":null,"preventionTips":["Use None for unlimited, never -1 sentinels","Clamp config-sourced window values at load time"],"tags":["kv-cache","sliding-window","argument-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}