{"record":{"id":"22cd68723e4b963b","repo":"sgl-project/sglang","slug":"invalid-kimi-image-grid-metadata-values-expect","errorCode":null,"errorMessage":"Invalid Kimi image grid metadata: {values}; expected [h, w] or [t, h, w]","messagePattern":"Invalid Kimi image grid metadata: (.+?); expected \\[h, w\\] or \\[t, h, w\\]","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/disaggregation/encoder/preprocessor.py","lineNumber":687,"sourceCode":"        if self.model_type == \"kimi_vl\" and modality == Modality.IMAGE:\n            h, w = self._kimi_hw_from_patch_grid(grid)\n            return h * w\n        return int(grid[0] * grid[1] * grid[2])\n\n    @staticmethod\n    def _kimi_hw_from_patch_grid(\n        grid: Union[torch.Tensor, np.ndarray, List[int], Tuple[int, ...]],\n    ) -> Tuple[int, int]:\n        \"\"\"Extract (height, width) from Kimi 2D or 3D patch-grid metadata.\"\"\"\n        if isinstance(grid, torch.Tensor):\n            values = grid.flatten().tolist()\n        elif isinstance(grid, np.ndarray):\n            values = grid.reshape(-1).tolist()\n        else:\n            values = np.asarray(grid).reshape(-1).tolist()\n\n        if len(values) not in (2, 3):\n            raise ValueError(\n                f\"Invalid Kimi image grid metadata: {values}; \"\n                \"expected [h, w] or [t, h, w]\"\n            )\n        return int(values[-2]), int(values[-1])\n\n    def _kimi_tokens_from_patch_grid(self, grid: Union[torch.Tensor, List[int]]) -> int:\n        \"\"\"Calculate Kimi image tokens from either 2D or 3D patch metadata.\"\"\"\n        h, w = self._kimi_hw_from_patch_grid(grid)\n        merge_h, merge_w = self.model_config.hf_config.vision_config.merge_kernel_size\n        return (h * w) // (merge_h * merge_w)\n\n    def get_num_tokens(\n        self, grid: Union[torch.Tensor, List[int]], modality: Modality\n    ) -> int:\n        \"\"\"Compatibility helper for callers that still provide patch grids.\"\"\"\n        if modality == Modality.AUDIO:\n            input_length = self.get_num_patches(grid, modality)\n            return self._get_feat_extract_output_lengths(input_length)","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/disaggregation/encoder/preprocessor.py#L669-L705","documentation":"Thrown by _kimi_hw_from_patch_grid when the flattened Kimi image grid metadata does not contain exactly 2 ([h, w]) or 3 ([t, h, w]) values. Kimi vision models attach a patch-grid tensor per image; h/w (and optional temporal t) are used to compute patch and token counts. Anything with a different element count (empty, scalar, 4+ values) is rejected.","triggerScenarios":"Calling get_num_patches / get_num_tokens / _kimi_tokens_from_patch_grid on a kimi_vl / kimi_k25 / kimi_k3 model with a grid that is a scalar (e.g. audio-style feature length), an empty tensor, or a per-batch stacked tensor that flattens to more than 3 values (e.g. [b, t, h, w]).","commonSituations":"Feeding a batched grid (multiple images concatenated) instead of a per-image grid; passing an unflattened nested list; a corrupted or truncated grid tensor from the disaggregation transfer path; reusing a generic grid format from another model family with Kimi.","solutions":["Pass a per-image grid with exactly 2 or 3 entries, e.g. torch.tensor([t, h, w]) or [h, w].","If you have a batched grid tensor, index it per image (grid[i]) before calling get_num_patches/get_num_tokens.","Print the offending values (included in the message) and verify the vision processor output shape for Kimi models.","Ensure the grid is not None/empty before calling."],"exampleFix":"# before\nh, w = prep._kimi_hw_from_patch_grid(batched_grid)  # shape (N, 3)\n# after\nfor g in batched_grid:\n    h, w = prep._kimi_hw_from_patch_grid(g)  # each shape (3,)","handlingStrategy":"validation","validationCode":"def is_valid_kimi_grid(grid) -> bool:\n    import numpy as np\n    if hasattr(grid, \"flatten\"):\n        vals = grid.flatten().tolist()\n    else:\n        vals = np.asarray(grid).reshape(-1).tolist()\n    return len(vals) in (2, 3)","typeGuard":"from typing import Union\nimport torch\n\ndef is_kimi_patch_grid(grid: Union[torch.Tensor, list, tuple]) -> bool:\n    try:\n        v = grid.flatten().tolist() if isinstance(grid, torch.Tensor) else list(grid)\n    except Exception:\n        return False\n    return len(v) in (2, 3) and all(isinstance(x, (int, float)) for x in v)","tryCatchPattern":"try:\n    h, w = prep._kimi_hw_from_patch_grid(grid)\nexcept ValueError:\n    raise ValueError(f\"expected per-image [h,w] or [t,h,w] grid, got shape {getattr(grid, 'shape', grid)}\")","preventionTips":["Slice batched grid tensors per image before calling get_num_patches/get_num_tokens.","Unit-test grid parsing with 2D and 3D per-image grids only.","Never pass scalar feature-length values (audio-style) into the Kimi image grid path."],"tags":["multimodal","kimi","grid-metadata","shape-validation"],"backgroundTag":"tensor-shape-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}