{"record":{"id":"0f8b56791fae6c1b","repo":"sgl-project/sglang","slug":"name-placeholder-data-mismatch-placeholder-cou","errorCode":null,"errorMessage":"{name} placeholder/data mismatch: {placeholder_count} placeholders vs {data_count} {name}s","messagePattern":"(.+?) placeholder/data mismatch: (.+?) placeholders vs (.+?) (.+?)s","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/mimo_v2.py","lineNumber":1521,"sourceCode":"        counts = {\n            Modality.IMAGE: 0,\n            Modality.VIDEO: 0,\n            Modality.AUDIO: 0,\n        }\n        for text_part in text_parts:\n            if multimodal_tokens_pattern.match(text_part):\n                modality = self.mm_tokens.get_modality_of_token(text_part)\n                if modality in counts:\n                    counts[modality] += 1\n\n        for modality, name, data_count in (\n            (Modality.IMAGE, \"image\", image_count),\n            (Modality.VIDEO, \"video\", video_count),\n            (Modality.AUDIO, \"audio\", audio_count),\n        ):\n            placeholder_count = counts[modality]\n            if placeholder_count != data_count:\n                raise ValueError(\n                    f\"{name} placeholder/data mismatch: \"\n                    f\"{placeholder_count} placeholders vs {data_count} {name}s\"\n                )\n\n    def __init__(self, hf_config, server_args, _processor, *args, **kwargs):\n        super().__init__(hf_config, server_args, _processor, *args, **kwargs)\n        self.vision_config = Qwen2_5_VLVisionConfig.from_dict(hf_config.vision_config)\n\n        patch_size = self.vision_config.patch_size\n        spatial_merge_size = getattr(self.vision_config, \"spatial_merge_size\", 2)\n        unit_size = patch_size * spatial_merge_size\n        self.image_factor = unit_size\n\n        rope_type = \"rope\"\n        rope_scaling = getattr(hf_config, \"rope_scaling\", None)\n        if rope_scaling:\n            if (\n                rope_scaling.get(\"type\", None) == \"default\"","sourceCodeStart":1503,"sourceCodeEnd":1539,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/mimo_v2.py#L1503-L1539","documentation":"Raised by _validate_placeholder_counts when the number of multimodal placeholder tokens embedded in the prompt text doesn't equal the number of supplied data items per modality (image, video, audio counted separately). It guards the text-template vs payload consistency of the request.","triggerScenarios":"Sending a prompt with, say, 3 <|image|> placeholders but only 2 image entries in images=[...]; or 1 video placeholder with 2 videos; likewise for audio. Any per-modality count mismatch between text template and data list triggers it.","commonSituations":"Templating bugs where the placeholder loop count drifts from the attachments list; client code appending images without adding tokens (or vice versa); copy-pasting prompts with leftover placeholders; audio placeholders missing when sending video-with-audio.","solutions":["Make placeholders and data lists generated from the same source: text = template.format per item, images=[...] of equal length","Count tokens per modality before submit and assert equality client-side","For video-with-audio, account for audio placeholders exactly as the model template expects"],"exampleFix":"# before\ntext = '<|image|><|image|><|image|> describe'\nimages = [img1, img2]           # 3 placeholders vs 2 images → ValueError\n# after\ntext = '<|image|>' * len(images) + ' describe'\nimages = [img1, img2]","handlingStrategy":"validation","validationCode":"import re\nfrom collections import Counter\ndef check(prompt, images=0, videos=0, audios=0):\n    c = Counter(re.findall(r'<\\|image\\|>', prompt))['<|image|>'] if images else 0\n    # count each modality's placeholder token and compare:\n    assert prompt.count(IMAGE_TOKEN) == images, f'{prompt.count(IMAGE_TOKEN)} vs {images}'\n    assert prompt.count(VIDEO_TOKEN) == videos\n    assert prompt.count(AUDIO_TOKEN) == audios\n\ncheck(text, images=len(images), videos=len(videos), audios=len(audios))","typeGuard":null,"tryCatchPattern":"try:\n    out = await epd.process_mm_data_async(text, images=images, videos=videos, audios=audios)\nexcept ValueError as e:\n    if 'placeholder/data mismatch' in str(e):\n        return error_response(400, str(e) + '; regenerate prompt from attachments')\n    raise","preventionTips":["Generate placeholder tokens programmatically: prompt = TOK * len(items) + question","Never hand-edit multimodal prompts","Add a client-side count assertion before every submit"],"tags":["validation","placeholders","prompt-template","multimodal"],"backgroundTag":"placeholder-count-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}