{"record":{"id":"a0f87763ee7f0455","repo":"sgl-project/sglang","slug":"group-concurrent-contiguous-requires-equal-length","errorCode":null,"errorMessage":"group_concurrent_contiguous requires equal-length src/dst index arrays, got {src_indices.size} and {dst_indices.size}","messagePattern":"group_concurrent_contiguous requires equal-length src/dst index arrays, got (.+?) and (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/disaggregation/common/utils.py","lineNumber":123,"sourceCode":"        dst_addr = dst_aux_ptr + item_len * aux_index\n        buffer = (ctypes.c_byte * len(data)).from_address(dst_addr)\n        buffer[:] = data\n        return\n\n\ndef group_concurrent_contiguous(\n    src_indices: npt.NDArray[np.int32], dst_indices: npt.NDArray[np.int32]\n) -> Tuple[List[npt.NDArray[np.int32]], List[npt.NDArray[np.int32]]]:\n    \"\"\"Vectorised NumPy implementation.\"\"\"\n    # src/dst indices are transferred pairwise, so an empty side means there is\n    # nothing to transfer. Guarding both sides (not just src) avoids a cryptic\n    # NumPy broadcast error from np.diff() below when only one side is empty, e.g.\n    # a non-empty prefill DSA/SWA state list paired with an empty decode registration.\n    if src_indices.size == 0 or dst_indices.size == 0:\n        return [], []\n\n    if src_indices.size != dst_indices.size:\n        raise ValueError(\n            \"group_concurrent_contiguous requires equal-length src/dst index arrays, \"\n            f\"got {src_indices.size} and {dst_indices.size}\"\n        )\n\n    brk = np.where((np.diff(src_indices) != 1) | (np.diff(dst_indices) != 1))[0] + 1\n    src_groups = np.split(src_indices, brk)\n    dst_groups = np.split(dst_indices, brk)\n\n    src_groups = [g.tolist() for g in src_groups]\n    dst_groups = [g.tolist() for g in dst_groups]\n\n    return src_groups, dst_groups\n\n\n@dataclasses.dataclass(frozen=True)\nclass DCPTokenTransferPlan:\n    src_token_indices: npt.NDArray[np.int64]\n    dst_token_indices: npt.NDArray[np.int64]","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/disaggregation/common/utils.py#L105-L141","documentation":"group_concurrent_contiguous groups src/dst page indices into concurrent contiguous runs for batched KV transfer. It requires src_indices.size == dst_indices.size (both non-empty; empty inputs short-circuit earlier). A length mismatch means the prefill-side page list and decode-side allocation list do not correspond 1:1, which would corrupt the transfer.","triggerScenarios":"Calling send_kvcache / _send_kvcache_generic / send_kvcache_dcp / _send_swa_dsa_state with page-index arrays of different lengths, e.g. prefill computed N pages but decode registration/allocations returned M != N indices.","commonSituations":"Prefill and decode disagree on prefix length or page count (e.g. chunked prefill boundary off-by-page); decode allocator returned fewer pages for a request; races where one side was updated for an aborted request. Also exercised directly in unit tests of the grouping helper.","solutions":["Log both arrays' lengths and content at the call site and reconcile which side is wrong (usually the decode allocation count vs prefill page count)","Ensure prefix/padding math produces identical token->page counts on both servers (same page_size, same padding)","If developing against the helper, pass arrays already validated as equal length"],"exampleFix":"# before\ngroup_concurrent_contiguous(src_pages, dst_pages)  # len mismatch -> ValueError\n# after\nassert src_pages.size == dst_pages.size, (src_pages.size, dst_pages.size)\ngroup_concurrent_contiguous(src_pages, dst_pages)","handlingStrategy":"validation","validationCode":"assert src_indices.size == dst_indices.size, (src_indices.size, dst_indices.size)","typeGuard":"def _valid_index_pair(src: np.ndarray, dst: np.ndarray) -> bool:\n    return src.size == dst.size and src.size > 0","tryCatchPattern":null,"preventionTips":["Assert equal page counts before every send_kvcache call","Keep page_size identical on both servers so token->page mapping matches"],"tags":["disaggregation","kv-transfer","index-mismatch","validation"],"backgroundTag":"index-length-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}