{"record":{"id":"4f75640cdd72e5b7","repo":"sgl-project/sglang","slug":"debug-name-cache-head-start-required-for-head-s","errorCode":null,"errorMessage":"{debug_name}: cache_head_start required for head slice","messagePattern":"(.+?): cache_head_start required for head slice","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/layers/kvcache/qvg_packed_cache.py","lineNumber":286,"sourceCode":"                    self._pack(s)\n\n    # -------------------------------------------------------------- contract\n    def update_and_get_attention_kv(\n        self,\n        *,\n        key: torch.Tensor,\n        value: torch.Tensor,\n        current_chunk_start: int,\n        cache_head_start: int | None = None,\n        recent_window_tokens: int | None = None,\n        debug_name: str = \"QVG packed KV cache\",\n    ) -> CausalAttentionKVView:\n        num_new = key.shape[1]\n        num_input_heads = key.shape[2]\n        head_slice = None\n        if num_input_heads != self.num_heads:\n            if cache_head_start is None:\n                raise ValueError(\n                    f\"{debug_name}: cache_head_start required for head slice\"\n                )\n            head_slice = slice(cache_head_start, cache_head_start + num_input_heads)\n        cend = current_chunk_start + num_new\n\n        if self._cur is not None and current_chunk_start == self._cur.g0:\n            # rewrite current chunk in place (denoise step)\n            if cend != self._cur.g1:\n                raise NotImplementedError(\n                    f\"{debug_name}: current-chunk rewrite size changed\"\n                )\n            self._write(self._cur, key, value, head_slice)\n        elif current_chunk_start == self._global_end:\n            # advance: finalize current chunk, start a new one\n            if self._cur is not None:\n                self._segments.append(self._cur)\n            is_sink = current_chunk_start < self.sink_tokens\n            if self._chunk_tokens == 0:","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/layers/kvcache/qvg_packed_cache.py#L268-L304","documentation":"Same head-slice contract as the dense cache, but for QVGPackedCausalKVCache: if the input key's head count differs from the cache's num_heads, a cache_head_start offset must be supplied to locate the head slice to write.","triggerScenarios":"update_and_get_attention_kv on the packed cache with num_input_heads != self.num_heads and cache_head_start=None.","commonSituations":"GQA/MQA-style layers or shared packed caches across layers with different KV head counts; new model wiring that forgot the head offset argument.","solutions":["Pass cache_head_start (first cache-head index for this writer)","Match input head count to the cache's num_heads if slicing is unintended","Check cache allocation uses the maximum head count across contributing layers"],"exampleFix":"# before\ncache.update_and_get_attention_kv(k, v, current_chunk_start=s)\n# after\ncache.update_and_get_attention_kv(k, v, current_chunk_start=s, cache_head_start=off)","handlingStrategy":"validation","validationCode":"if key.shape[2] != cache.num_heads:\n    assert cache_head_start is not None","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass cache_head_start for every partial-head writer","Keep cache num_heads = max across layers"],"tags":["kv-cache","gqa","head-slicing","argument-validation"],"backgroundTag":"kv-cache-shape-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}