{"record":{"id":"1e23e14feb4a1637","repo":"hiyouga/LlamaFactory","slug":"the-number-of-images-does-not-match-the-number-of","errorCode":null,"errorMessage":"The number of images does not match the number of {IMAGE_PLACEHOLDER} tokens in {messages}.","messagePattern":"The number of images does not match the number of (.+?) tokens in (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/data/mm_plugin.py","lineNumber":204,"sourceCode":"        if self.audio_token is not None and feature_extractor is None:\n            raise ValueError(\"Audio feature extractor was not found, please check and update your model file.\")\n\n    def _validate_messages(\n        self,\n        messages: list[dict[str, str]],\n        images: list[\"ImageInput\"],\n        videos: list[\"VideoInput\"],\n        audios: list[\"AudioInput\"],\n    ):\n        r\"\"\"Validate if the number of images, videos and audios match the number of placeholders in messages.\"\"\"\n        num_image_tokens, num_video_tokens, num_audio_tokens = 0, 0, 0\n        for message in messages:\n            num_image_tokens += message[\"content\"].count(IMAGE_PLACEHOLDER)\n            num_video_tokens += message[\"content\"].count(VIDEO_PLACEHOLDER)\n            num_audio_tokens += message[\"content\"].count(AUDIO_PLACEHOLDER)\n\n        if len(images) != num_image_tokens:\n            raise ValueError(\n                f\"The number of images does not match the number of {IMAGE_PLACEHOLDER} tokens in {messages}.\"\n            )\n\n        if len(videos) != num_video_tokens:\n            raise ValueError(\n                f\"The number of videos does not match the number of {VIDEO_PLACEHOLDER} tokens in {messages}.\"\n            )\n\n        if len(audios) != num_audio_tokens:\n            raise ValueError(\n                f\"The number of audios does not match the number of {AUDIO_PLACEHOLDER} tokens in {messages}.\"\n            )\n\n    def _preprocess_image(\n        self, image: \"ImageObject\", image_max_pixels: int, image_min_pixels: int, **kwargs\n    ) -> \"ImageObject\":\n        r\"\"\"Pre-process a single image.\"\"\"\n        if (image.width * image.height) > image_max_pixels:","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/data/mm_plugin.py#L186-L222","documentation":"ValueError from BasePlugin._validate_messages: the number of image files supplied for a sample does not equal the number of <image> placeholders found across the messages' content strings. The plugin counts IMAGE_PLACEHOLDER occurrences per message and compares with len(images); any mismatch aborts preprocessing of that sample.","triggerScenarios":"A multimodal row whose 'images' list length differs from the count of <image> tags in the conversations (e.g. two placeholders but one image path, or placeholders removed but images kept); also placeholder typos such as < image > that silently fail to count.","commonSituations":"Hand-edited conversation files where an <image> tag was deleted or added; datasets converted from other formats that append a default image to every turn; multiple images per turn without matching placeholder counts; whitespace-corrupted tags.","solutions":["For every sample, make count('<image>' in all message contents) == len(images list); add or remove placeholders/files accordingly.","Sanitize placeholders: ensure exact '<image>' spelling with no extra spaces.","Use exactly one <image> per turn when a single image is shared, or split the image list to match multiple placeholders.","Write a pre-check script over the JSONL that reports mismatched rows before training."],"exampleFix":"# before\n{\"images\": [\"a.jpg\"], \"conversations\": [{\"from\": \"human\", \"value\": \"<image> Compare <image>\"}]}\n\n# after\n{\"images\": [\"a.jpg\", \"b.jpg\"], \"conversations\": [{\"from\": \"human\", \"value\": \"<image> Compare <image>\"}]}","handlingStrategy":"validation","validationCode":"IMAGE_PH = \"<image>\"\n\ndef mismatched_image_rows(rows: list[dict]) -> list[int]:\n    bad = []\n    for i, r in enumerate(rows):\n        n = sum(m.get(\"value\", m.get(\"content\", \"\")).count(IMAGE_PH) for m in r.get(\"conversations\", []))\n        if n != len(r.get(\"images\", [])):\n            bad.append(i)\n    return bad","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make image-list length == placeholder count a dataset ingestion invariant.","Generate <image> tags programmatically when building rows.","Watch for spaces inside tags ('< image >') which break counting."],"tags":["multimodal","image","data-format","placeholders"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}