{"record":{"id":"6509cbb2199de06b","repo":"sgl-project/sglang","slug":"context-expected-2d-positions-got-shape-tuple","errorCode":null,"errorMessage":"{context} expected 2D positions, got shape={tuple(pos2d.shape)}.","messagePattern":"(.+?) expected 2D positions, got shape=(.+?)\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/speculative/dflash_worker_v2.py","lineNumber":705,"sourceCode":"\n    def clear_cache_pool(self):\n        # The target worker owns the shared KV allocator/cache. For the compact\n        # sliding-window path, the draft req->token view is rebuilt from committed\n        # 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                )","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/speculative/dflash_worker_v2.py#L687-L723","documentation":"Raised by the DFLASH v2 worker's gather helper when the positions tensor passed for a req_to_token gather is not 2D. The masked gather API expects positions laid out as (num_segments_or_rows, positions_per_row) so the mask can be compared elementwise. Any 1D flattened or 3D tensor triggers this internal invariant error.","triggerScenarios":"Internal code paths (_gather_req_to_token_segments callers) passing flattened 1D position indices or a batched 3D tensor; almost always a bug in the worker's own batch assembly rather than user config.","commonSituations":"Refactors of the DFLASH worker's position bookkeeping; changes to how prefill/decode positions are computed; custom speculative schedules feeding non-2D positions.","solutions":["Inspect the caller of _gather_req_to_token_segments to see what shape it builds for pos2d and restore the (rows, cols) layout","Add a shape assert where positions are constructed to catch divergence earlier","If you modified sglang speculative code, re-check against upstream — this is an internal invariant, not a config knob"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"assert pos2d.ndim == 2, f\"positions must be 2D, got {pos2d.ndim}D\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep positions in (rows, cols) layout end-to-end; avoid ad-hoc flatten/view calls in speculative code","When patching sglang worker internals, add shape asserts at construction sites"],"tags":["sglang","speculative-decoding","dflash","internal-invariant","shape-mismatch"],"backgroundTag":"tensor-shape-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}