{"record":{"id":"b347f1aa7941016f","repo":"sgl-project/sglang","slug":"name-block-cnt-and-name-block-idx-must-both-be","errorCode":null,"errorMessage":"{name}_block_cnt and {name}_block_idx must both be provided or both be None","messagePattern":"(.+?)_block_cnt and (.+?)_block_idx must both be provided or both be None","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/kernels/ops/attention/flash_attn/cute/block_sparsity.py","lineNumber":242,"sourceCode":"        hint_clause = f\" Hint: {resolved_hint}\" if resolved_hint else \"\"\n        raise ValueError(\n            f\"{tensor_name}{context_clause} with shape {tensor.shape} cannot be expanded to expected shape {expected_shape}.\"\n            f\"{hint_clause}\"\n        )\n    return tensor.expand(*expected_shape)\n\n\ndef _check_and_expand_block(\n    name: str,\n    cnt: torch.Tensor | None,\n    idx: torch.Tensor | None,\n    expected_count_shape: Tuple[int, ...],\n    expected_index_shape: Tuple[int, ...],\n    context: str | None,\n    hint: str | Callable[[], str] | None,\n) -> Tuple[torch.Tensor | None, torch.Tensor | None]:\n    if (cnt is None) != (idx is None):\n        raise ValueError(\n            f\"{name}_block_cnt and {name}_block_idx must both be provided or both be None\"\n        )\n    if cnt is None or idx is None:\n        return None, None\n    if cnt.dtype != torch.int32 or idx.dtype != torch.int32:\n        raise ValueError(f\"{name}_block tensors must have dtype torch.int32\")\n    if cnt.device != idx.device:\n        raise ValueError(\n            f\"{name}_block_cnt and {name}_block_idx must be on the same device\"\n        )\n    if not cnt.is_cuda or not idx.is_cuda:\n        raise ValueError(f\"{name}_block tensors must live on CUDA\")\n    expanded_cnt = _expand_sparsity_tensor(\n        cnt, expected_count_shape, f\"{name}_block_cnt\", context, hint\n    )\n    # [Note] Allow Compact block sparse indices\n    # Allow the last dimension (n_blocks) of idx to be <= expected, since\n    # FA4 only accesses indices 0..cnt-1 per query tile. This enables compact","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/kernels/ops/attention/flash_attn/cute/block_sparsity.py#L224-L260","documentation":"Block-sparsity metadata comes in pairs: {name}_block_cnt and {name}_block_idx. _check_and_expand_block enforces that both are provided together or both are None; supplying only one raises ValueError.","triggerScenarios":"Calling normalize_block_sparse_tensors (or FA with block-sparse args) with e.g. topk_block_cnt set but topk_block_idx=None, or vice versa.","commonSituations":"Building only the count tensor and forgetting the index tensor; partial refactors of sparsity metadata plumbing; optional-arg defaults left None on one side.","solutions":["Provide both tensors or neither","Check call site: if cnt is not None: assert idx is not None"],"exampleFix":"# before\nres = normalize_block_sparse_tensors(topk_cnt, None, ...)\n# after\nres = normalize_block_sparse_tensors(topk_cnt, topk_idx, ...)","handlingStrategy":"validation","validationCode":"assert (cnt is None) == (idx is None), 'cnt and idx must be provided together'","typeGuard":"def valid_block_pair(cnt, idx) -> bool:\\n    return (cnt is None) == (idx is None)","tryCatchPattern":null,"preventionTips":["Keep cnt/idx as a single dataclass or tuple through the codebase","Add a paired-argument assert at the public API entry"],"tags":["block-sparse","attention","paired-arguments","validation"],"backgroundTag":"paired-arguments-both-required","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}