{"record":{"id":"abc6cfb1d7fdf065","repo":"sgl-project/sglang","slug":"source-and-destination-groups-must-have-the-same-l","errorCode":null,"errorMessage":"Source and destination groups must have the same length","messagePattern":"Source and destination groups must have the same length","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/disaggregation/mori/conn.py","lineNumber":261,"sourceCode":"    bytes_per_token_src: int\n    bytes_per_token_dst: int\n    src_head_slice_offset: int\n    dst_head_slice_offset: int\n    heads_bytes_per_token_to_send: int\n\n\n@dataclasses.dataclass(frozen=True)\nclass GroupedIndexPlan:\n    src_starts: List[int]\n    dst_starts: List[int]\n    counts: List[int]\n\n    @classmethod\n    def from_groups(\n        cls, src_groups: List[List[int]], dst_groups: List[List[int]]\n    ) -> GroupedIndexPlan:\n        if len(src_groups) != len(dst_groups):\n            raise ValueError(\"Source and destination groups must have the same length\")\n        return cls(\n            src_starts=[int(group[0]) for group in src_groups],\n            dst_starts=[int(group[0]) for group in dst_groups],\n            counts=[len(group) for group in src_groups],\n        )\n\n    def materialize(self, item_len: int) -> BatchTransferPlan:\n        return BatchTransferPlan(\n            local_offsets=[start * item_len for start in self.src_starts],\n            remote_offsets=[start * item_len for start in self.dst_starts],\n            sizes=[count * item_len for count in self.counts],\n        )\n\n\n@dataclasses.dataclass(frozen=True)\nclass BatchTransferPlan:\n    local_offsets: List[int]\n    remote_offsets: List[int]","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/disaggregation/mori/conn.py#L243-L279","documentation":"GroupedIndexPlan.from_groups builds a compact (start,count) transfer plan from paired source/destination index groups; unequal group counts mean the pairing is malformed and it refuses with ValueError.","triggerScenarios":"Calling from_groups with len(src_groups) != len(dst_groups) — e.g. when slicing SWA/DSA state groups or building kv transfer plans where one side yielded fewer groups.","commonSituations":"Layer or rank slicing bugs that drop groups on one side; mismatched layer counts between PD configurations.","solutions":["Log and compare len(src_groups) vs len(dst_groups) before building the plan","Check layer-range config (prefill_start_layer / pp slicing) produces symmetric groups on both sides","Fix upstream slicing that produced the asymmetry"],"exampleFix":"assert len(src_groups) == len(dst_groups), (len(src_groups), len(dst_groups))\nplan = GroupedIndexPlan.from_groups(src_groups, dst_groups)","handlingStrategy":"validation","validationCode":"if len(src_groups) != len(dst_groups):\n    raise ValueError(f\"group mismatch: {len(src_groups)} vs {len(dst_groups)}\")\nplan = GroupedIndexPlan.from_groups(src_groups, dst_groups)","typeGuard":null,"tryCatchPattern":"catch ValueError from from_groups; log both group lists' lengths per layer to find the asymmetric slice","preventionTips":["Assert symmetric layer slicing in tests","Property-test from_groups with mirrored inputs"],"tags":["mori","index-plan","validation"],"backgroundTag":"collection-length-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}