{"record":{"id":"9fee4d8c2007d98e","repo":"sgl-project/sglang","slug":"context-mask-position-shape-mismatch-tuple-mas","errorCode":null,"errorMessage":"{context} mask/position shape mismatch: {tuple(mask.shape)} vs {tuple(pos2d.shape)}.","messagePattern":"(.+?) mask/position shape mismatch: (.+?) vs (.+?)\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/speculative/dflash_worker_v2.py","lineNumber":709,"sourceCode":"        # target state before each draft forward, so there is nothing persistent\n        # to flush here.\n        pass\n\n    def _gather_req_to_token_masked(\n        self,\n        *,\n        req_to_token: torch.Tensor,\n        req_pool_indices: torch.Tensor,\n        pos2d: torch.Tensor,\n        mask: torch.Tensor,\n        context: str,\n    ) -> torch.Tensor:\n        if pos2d.ndim != 2:\n            raise RuntimeError(\n                f\"{context} expected 2D positions, got shape={tuple(pos2d.shape)}.\"\n            )\n        if mask.shape != pos2d.shape:\n            raise RuntimeError(\n                f\"{context} mask/position shape mismatch: {tuple(mask.shape)} vs {tuple(pos2d.shape)}.\"\n            )\n\n        if req_pool_indices.dtype != torch.int64:\n            req_pool_indices = req_pool_indices.to(torch.int64)\n        if mask.dtype != torch.bool:\n            mask = mask.to(torch.bool)\n\n        table_width = int(req_to_token.shape[1])\n        if table_width <= 0:\n            if bool(mask.any().item()):\n                raise RuntimeError(\n                    f\"{context} req_to_token table is empty but gather mask is non-empty.\"\n                )\n            return torch.empty((0,), dtype=torch.int64, device=self.device)\n\n        # Only the masked-off rectangular padding can be out of range in the normal\n        # ragged-batch case. Replace those don't-care columns with a valid in-range","sourceCodeStart":691,"sourceCodeEnd":727,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/speculative/dflash_worker_v2.py#L691-L727","documentation":"Raised when the boolean gather mask and the 2D positions tensor have different shapes in the DFLASH worker's req_to_token gather. The mask selects which position entries are real vs padding, so it must be elementwise-aligned with pos2d. A mismatch means the caller built the mask from a different batch layout than the positions.","triggerScenarios":"Building the mask from a (bs, draft_token_num) layout while positions were reshaped to (total_tokens, 1), or any caller that computes mask and positions from different sources/sizes. Internal worker bug, not user-facing config.","commonSituations":"Editing the worker's segment assembly so mask and positions are built in separate loops that diverge; schedule changes (variable draft lengths) updating one but not the other.","solutions":["Find the caller building both mask and pos2d and derive the mask from pos2d itself (e.g. valid_mask = pos2d >= 0) so they can't diverge","Log tuple(mask.shape) and tuple(pos2d.shape) at the call site to identify which layout is stale","Diff against upstream sglang if local changes were made to segment gathering"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"assert mask.shape == pos2d.shape, (\n    f\"mask {tuple(mask.shape)} != positions {tuple(pos2d.shape)}\"\n)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive the mask from pos2d (e.g. pos2d >= 0) instead of computing it independently","Log both shapes at segment assembly when developing custom schedules"],"tags":["sglang","speculative-decoding","dflash","internal-invariant","mask-shape"],"backgroundTag":"tensor-shape-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}