sgl-project/sglang · error · ValueError
out, max_logits and lse must not alias each other
Error message
out, max_logits and lse must not alias each other
What it means
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.
Source
Thrown at python/sglang/kernels/ops/attention/sparse_mla_q8kv8_prefill_sm90.py:460
_check_out_buffer(out, "out", (s_q, h_q, d_v), torch.bfloat16, device)
if max_logits is None:
max_logits = torch.empty(s_q, h_q, dtype=torch.float32, device=device)
else:
_check_out_buffer(max_logits, "max_logits", (s_q, h_q), torch.float32, device)
if lse is None:
lse = torch.empty(s_q, h_q, dtype=torch.float32, device=device)
else:
_check_out_buffer(lse, "lse", (s_q, h_q), torch.float32, device)
# The three output tensors are written independently by the kernel; any
# aliasing among them would corrupt results, so reject it explicitly.
out_ptr = out.data_ptr()
ml_ptr = max_logits.data_ptr()
lse_ptr = lse.data_ptr()
if out_ptr == ml_ptr or out_ptr == lse_ptr or ml_ptr == lse_ptr:
raise ValueError("out, max_logits and lse must not alias each other")
cuda_stream = _get_current_stream_raw(q.device.index)
if attn_sink is not None and topk_length is not None:
_sparse_mla_q8kv8_prefill_full_op(
q,
kv,
indices,
q_scale,
kv_scale,
attn_sink,
topk_length,
out,
max_logits,
lse,
s_q,
s_kv,
h_q,View on GitHub (pinned to 0132848349)
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)
Example fix
// before buf = torch.empty(s_q*h_q*(d_v+2), ...) out, max_logits, lse = buf[:a], buf[a:b], buf[b:] # or worse, aliased // after out = torch.empty((s_q, h_q, d_v), dtype=torch.bfloat16, device=device) max_logits = torch.empty((s_q, h_q), dtype=torch.float32, device=device) lse = torch.empty((s_q, h_q), dtype=torch.float32, device=device)
Defensive patterns
Strategy: validation
Validate before calling
ptrs = {out.data_ptr(), max_logits.data_ptr(), lse.data_ptr()}
assert len(ptrs) == 3, 'output buffers must not alias' Type guard
def distinct_buffers(*ts) -> bool:
return len({t.data_ptr() for t in ts}) == len(ts) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- sparse_mla_q8kv8_prefill_fwd requires h_kv=1, got {h_kv}
- attn_sink must be float32 with shape ({h_q},), got {tuple(at
- attn_sink must be a CUDA tensor
- attn_sink must be on q's device {device}, got {attn_sink.dev
- attn_sink must be contiguous
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/0c54b6399bda9873.
Report an issue: GitHub.