{"record":{"id":"3d15c37e92e962a3","repo":"sgl-project/sglang","slug":"prompt-has-num-placeholders-image-placeholder-to","errorCode":null,"errorMessage":"prompt has {num_placeholders} image placeholder token(s) but {len(counts)} image(s) were provided","messagePattern":"prompt has (.+?) image placeholder token\\(s\\) but (.+?) image\\(s\\) were provided","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/base_processor.py","lineNumber":1631,"sourceCode":"        counts: List[int],\n        placeholder_token_id: Optional[int],\n    ) -> List[int]:\n        \"\"\"Rebuild final input_ids for a pre-tokenized (list[int]) prompt.\n\n        Keep the user's ORIGINAL tokens verbatim and expand the i-th image\n        placeholder into ``counts[i]`` copies of ``placeholder_token_id``. The HF\n        processor's re-tokenization is discarded, so non-media tokens cannot\n        drift.\n\n        \"\"\"\n        if placeholder_token_id is None:\n            raise ValueError(\"placeholder_token_id is not set for this processor\")\n\n        num_placeholders = sum(\n            1 for token_id in original_ids if token_id == placeholder_token_id\n        )\n        if num_placeholders != len(counts):\n            raise ValueError(\n                f\"prompt has {num_placeholders} image placeholder token(s) but \"\n                f\"{len(counts)} image(s) were provided\"\n            )\n\n        rebuilt: List[int] = []\n        next_image_idx = 0\n        for token_id in original_ids:\n            if token_id == placeholder_token_id:\n                rebuilt.extend([placeholder_token_id] * counts[next_image_idx])\n                next_image_idx += 1\n            else:\n                rebuilt.append(token_id)\n        return rebuilt\n\n    def process_and_combine_mm_data(\n        self,\n        base_output: BaseMultiModalProcessorOutput,\n        mm_tokens: MultimodalSpecialTokens,","sourceCodeStart":1613,"sourceCodeEnd":1649,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/base_processor.py#L1613-L1649","documentation":"_expand_input_ids verifies that the number of placeholder token ids in the already-tokenized prompt exactly equals the number of expansion 'counts' (per-image expanded token counts). A mismatch means the prompt text and the media list disagree, so retokenize-avoidance expansion cannot proceed safely.","triggerScenarios":"Calling process_and_combine_mm_data where the tokenized prompt contains N placeholder tokens but the resolved per-image counts list has a different length — e.g. prompt with 2 <image> tags but 3 images provided, or custom text embedding extra placeholder tokens.","commonSituations":"Chat templates that add/remove placeholder tokens conditionally; offline tokenized prompts reused with different image counts; duplicated placeholder tokens from string interpolation.","solutions":["Regenerate the prompt with the model's chat template so placeholders match len(images) exactly","Ensure the tokenized ids correspond to the same prompt/media pairing used to compute counts","Avoid manually inserting or deleting placeholder tokens in the token ids"],"exampleFix":"// before\ninput_ids = tokenizer('<image> <image>')  # 2 placeholders\ncounts = [c for c in per_img]  # 3 images\n// after\ninput_ids = tokenizer('<image>' * len(images))\ncounts = [c for c in per_img]  # len == len(images) == placeholders","handlingStrategy":"validation","validationCode":"num_ph = sum(1 for t in input_ids if t == placeholder_token_id)\nassert num_ph == len(counts), f'{num_ph} placeholders vs {len(counts)} images'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always build the prompt via the chat template with the same media list used for counts","Never splice extra placeholder tokens into pre-tokenized ids"],"tags":["multimodal","tokenization","placeholder-mismatch","input-validation"],"backgroundTag":"placeholder-count-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}