{"record":{"id":"0c54b6399bda9873","repo":"sgl-project/sglang","slug":"out-max-logits-and-lse-must-not-alias-each-other","errorCode":null,"errorMessage":"out, max_logits and lse must not alias each other","messagePattern":"out, max_logits and lse must not alias each other","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/kernels/ops/attention/sparse_mla_q8kv8_prefill_sm90.py","lineNumber":460,"sourceCode":"        _check_out_buffer(out, \"out\", (s_q, h_q, d_v), torch.bfloat16, device)\n\n    if max_logits is None:\n        max_logits = torch.empty(s_q, h_q, dtype=torch.float32, device=device)\n    else:\n        _check_out_buffer(max_logits, \"max_logits\", (s_q, h_q), torch.float32, device)\n\n    if lse is None:\n        lse = torch.empty(s_q, h_q, dtype=torch.float32, device=device)\n    else:\n        _check_out_buffer(lse, \"lse\", (s_q, h_q), torch.float32, device)\n\n    # The three output tensors are written independently by the kernel; any\n    # aliasing among them would corrupt results, so reject it explicitly.\n    out_ptr = out.data_ptr()\n    ml_ptr = max_logits.data_ptr()\n    lse_ptr = lse.data_ptr()\n    if out_ptr == ml_ptr or out_ptr == lse_ptr or ml_ptr == lse_ptr:\n        raise ValueError(\"out, max_logits and lse must not alias each other\")\n\n    cuda_stream = _get_current_stream_raw(q.device.index)\n\n    if attn_sink is not None and topk_length is not None:\n        _sparse_mla_q8kv8_prefill_full_op(\n            q,\n            kv,\n            indices,\n            q_scale,\n            kv_scale,\n            attn_sink,\n            topk_length,\n            out,\n            max_logits,\n            lse,\n            s_q,\n            s_kv,\n            h_q,","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/kernels/ops/attention/sparse_mla_q8kv8_prefill_sm90.py#L442-L478","documentation":"The sparse MLA prefill kernel writes out, max_logits and lse through three independent pointers; if any two alias the same storage, one write would clobber another's results. The wrapper compares data_ptr() of the three buffers and rejects any overlap before launch.","triggerScenarios":"Passing caller-provided buffers where, e.g., lse and max_logits were carved from one allocation (out=lse=some_tensor, or overlapping views of one flat buffer), so any pair of data_ptr() values is equal.","commonSituations":"Pre-allocating one workspace tensor and slicing out the three outputs to save memory, or accidentally passing the same tensor twice when wiring up custom output buffers.","solutions":["Allocate three separate tensors: torch.empty(...) for out, max_logits and lse independently","If slicing a pooled buffer, ensure the byte ranges do not overlap (distinct offsets with sufficient sizes)"],"exampleFix":"// before\nbuf = torch.empty(s_q*h_q*(d_v+2), ...)\nout, max_logits, lse = buf[:a], buf[a:b], buf[b:]  # or worse, aliased\n// after\nout = torch.empty((s_q, h_q, d_v), dtype=torch.bfloat16, device=device)\nmax_logits = torch.empty((s_q, h_q), dtype=torch.float32, device=device)\nlse = torch.empty((s_q, h_q), dtype=torch.float32, device=device)","handlingStrategy":"validation","validationCode":"ptrs = {out.data_ptr(), max_logits.data_ptr(), lse.data_ptr()}\nassert len(ptrs) == 3, 'output buffers must not alias'","typeGuard":"def distinct_buffers(*ts) -> bool:\n    return len({t.data_ptr() for t in ts}) == len(ts)","tryCatchPattern":null,"preventionTips":["Always allocate out, max_logits, lse as three independent torch.empty calls","If pooling from one buffer, slice non-overlapping ranges and assert data_ptr uniqueness"],"tags":["mla","sparse-attention","buffer-aliasing","output-buffers"],"backgroundTag":"output-buffer-aliasing","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}