{"record":{"id":"015041fcc4da436a","repo":"sgl-project/sglang","slug":"invalid-head-config-inferred-from-mixed-qkv-h-h-015041","errorCode":null,"errorMessage":"Invalid head config inferred from mixed_qkv: H={H}, HV={HV}.","messagePattern":"Invalid head config inferred from mixed_qkv: H=(.+?), HV=(.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/kernels/ops/attention/helion/kda_decode.py","lineNumber":327,"sourceCode":"            f\"`out` must have shape {(B, 1, HV, V)} (got out.shape={tuple(out.shape)}).\"\n        )\n\n    qkv_dim = mixed_qkv.shape[1]\n    qk_dim = qkv_dim - HV * V\n    if qk_dim <= 0 or qk_dim % 2 != 0:\n        raise ValueError(\n            f\"Invalid packed `mixed_qkv` last dim={qkv_dim} for HV={HV}, V={V}.\"\n        )\n    q_dim = qk_dim // 2\n    if q_dim % K != 0:\n        raise ValueError(\n            f\"Invalid packed Q size {q_dim}: must be divisible by K={K}. \"\n            \"KDA packed decode requires num_q_heads == num_k_heads and \"\n            \"head_q_dim == head_k_dim.\"\n        )\n    H = q_dim // K\n    if H <= 0 or HV % H != 0:\n        raise ValueError(\n            f\"Invalid head config inferred from mixed_qkv: H={H}, HV={HV}.\"\n        )\n    return B, H, HV, K, V\n\n\ndef helion_fused_recurrent_kda_packed_decode(\n    mixed_qkv: torch.Tensor,\n    a: torch.Tensor,\n    b: torch.Tensor,\n    A_log: torch.Tensor,\n    dt_bias: torch.Tensor,\n    scale: float,\n    initial_state: torch.Tensor,\n    out: torch.Tensor,\n    ssm_state_indices: torch.Tensor,\n    use_qk_l2norm_in_kernel: bool = False,\n    lower_bound: float | None = None,\n) -> tuple[torch.Tensor, torch.Tensor]:","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/kernels/ops/attention/helion/kda_decode.py#L309-L345","documentation":"validate_packed_decode_inputs infers the query head count H = q_dim // K from mixed_qkv and requires H > 0 and HV % H == 0 (each query head maps to an integral group of value heads). This final check guarantees the head-layout is consistent before returning (B, H, HV, K, V) to the kernel launchers.","triggerScenarios":"A mixed_qkv whose Q width implies H that does not divide HV, e.g. H=3 with HV=4; degenerate q width yielding H=0 after the K-divisibility check passed via rounding.","commonSituations":"Hand-crafted test tensors with arbitrary widths; a model config where num_q_heads and num_v_heads are coprime; a corrupted projection weight producing an unexpected qkv width.","solutions":["Make num_q_heads divide num_v_heads (or equal it) in the model config","Recompute mixed_qkv from the actual projections instead of slicing/manually assembling it","Unit-test the head inference: assert HV % (q_dim // K) == 0 in your model tests"],"exampleFix":"// before\ncfg.num_q_heads = 3; cfg.num_v_heads = 4  # 4 % 3 != 0\n// after\ncfg.num_q_heads = 2; cfg.num_v_heads = 4  # 4 % 2 == 0","handlingStrategy":"validation","validationCode":"HV, V, K = initial_state.shape[-3:]\nq_dim = (mixed_qkv.shape[1] - HV * V) // 2\nH = q_dim // K\nassert H > 0 and HV % H == 0, (H, HV)","typeGuard":"def valid_head_layout(qkv: torch.Tensor, hv: int, v: int, k: int) -> bool:\n    q_dim = (qkv.shape[1] - hv * v) // 2\n    h = q_dim // k\n    return q_dim % k == 0 and h > 0 and hv % h == 0","tryCatchPattern":null,"preventionTips":["Choose num_q_heads that divides num_v_heads","Add a model-config sanity test covering head divisibility"],"tags":["kda","helion","head-config","shape-validation"],"backgroundTag":"unsupported-head-dimension","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}