{"record":{"id":"fc9f574fcec7cef3","repo":"sgl-project/sglang","slug":"kimi-image-placeholders-must-map-one-to-one-to-ima","errorCode":null,"errorMessage":"Kimi image placeholders must map one-to-one to image data: expected {expected_image_count}, found {placeholder_count} token(s)","messagePattern":"Kimi image placeholders must map one-to-one to image data: expected (.+?), found (.+?) token\\(s\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/kimi_k25.py","lineNumber":561,"sourceCode":"        # HF processor here would silently bypass Kimi's GPU preprocessing.\n        super().__init__(hf_config, server_args, processor, *args, **kwargs)\n        self.mm_tokens = mm_tokens\n\n    async def process_mm_data_async(\n        self,\n        image_data: List[Union[str, bytes, Dict]],\n        input_text,\n        request_obj,\n        *args,\n        **kwargs,\n    ):\n        expected_image_count = len(image_data or [])\n        placeholder_count = self.count_image_placeholders(\n            input_text, self.mm_tokens.image_token_id\n        )\n        if placeholder_count is not None:\n            if placeholder_count != expected_image_count:\n                raise ValueError(\n                    \"Kimi image placeholders must map one-to-one to image data: \"\n                    f\"expected {expected_image_count}, found {placeholder_count} token(s)\"\n                )\n            base_output = await self.fast_load_mm_data(\n                prompt=input_text,\n                image_data=image_data,\n                multimodal_tokens=self.mm_tokens,\n                # fast_load_mm_data, unlike load_mm_data, does not derive\n                # input_ids from the prompt; without this the wrapper falls back\n                # to re-tokenizing the expanded string.\n                input_ids=input_text,\n            )\n        else:\n            base_output = await self.load_mm_data(\n                prompt=input_text,\n                image_data=image_data,\n                multimodal_tokens=self.mm_tokens,\n            )","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/kimi_k25.py#L543-L579","documentation":"The Kimi K2.5 processor enforces a one-to-one mapping between image placeholders in the text and image_data entries. count_image_placeholders found a placeholder count different from len(image_data), so it fails before loading.","triggerScenarios":"Calling process_mm_data_async with input_text whose image placeholder token count != len(image_data) (count_image_placeholders returned non-None and mismatched).","commonSituations":"Client sends 3 images but the templated prompt has 2 placeholders (or vice versa), or a prompt with zero placeholders while still passing image_data.","solutions":["Align the number of image placeholder tokens in input_text with len(image_data)","Regenerate the prompt with the official chat template for the current image list","If placeholders are absent by design, send no image_data"],"exampleFix":"# before\nout = await proc.process_mm_data_async(\n    prompt=f\"{IMG} compare these\",\n    image_data=[img1, img2],\n)\n\n# after\nout = await proc.process_mm_data_async(\n    prompt=f\"{IMG} {IMG} compare these\",\n    image_data=[img1, img2],\n)","handlingStrategy":"validation","validationCode":"n = proc.count_image_placeholders(input_text, proc.mm_tokens.image_token_id)\nn_img = len(image_data or [])\nassert n is None or n == n_img, f\"{n} placeholders vs {n_img} images\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always render prompts with the official chat template","Update placeholders whenever the image list changes","Catch ValueError and surface a 4xx-style client error, not a retryable one"],"tags":["kimi","k25","multimodal","placeholder-count","validation"],"backgroundTag":"multimodal-placeholder-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}