{"record":{"id":"a1581915d09e74be","repo":"sgl-project/sglang","slug":"mm-content-hashes-has-len-image-hashes-entries","errorCode":null,"errorMessage":"mm_content_hashes has {len(image_hashes)} entries for {image_index} images","messagePattern":"mm_content_hashes has (.+?) entries for (.+?) images","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/disaggregation/encoder/receiver.py","lineNumber":2384,"sourceCode":"                            mm_item.content_hash\n                            if isinstance(mm_item, ImageData)\n                            else (\n                                mm_item.get(\"content_hash\")\n                                if isinstance(mm_item, dict)\n                                else None\n                            )\n                        )\n                        explicit_hash = (\n                            image_hashes[image_index]\n                            if image_hashes is not None\n                            and image_index < len(image_hashes)\n                            else None\n                        )\n                        entry[\"content_hash\"] = explicit_hash or inline_hash\n                        image_index += 1\n                    mm_data.append(entry)\n        if image_hashes is not None and image_index != len(image_hashes):\n            raise ValueError(\n                f\"mm_content_hashes has {len(image_hashes)} entries for \"\n                f\"{image_index} images\"\n            )\n        return mm_data\n\n\nclass MMReceiverHTTP(MMReceiverBase):\n    def __init__(\n        self,\n        server_args: ServerArgs,\n        dtype: Optional[torch.dtype] = None,\n        hf_config: Optional[PretrainedConfig] = None,\n        pp_rank: Optional[int] = None,\n        tp_rank: Optional[int] = None,\n        tp_group: Optional[GroupCoordinator] = None,\n        scheduler: Optional[\"Scheduler\"] = None,\n        encode_urls: Optional[List[str]] = None,\n    ):","sourceCodeStart":2366,"sourceCodeEnd":2402,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/disaggregation/encoder/receiver.py#L2366-L2402","documentation":"Raised while building mm_data on the receiver when the request's mm_content_hashes list length does not match the number of image entries actually collected from image_data. Hashes are indexed positionally per image (videos/audio are skipped), so any mismatch means the client's hash list is inconsistent with the image payload and cache lookups would be wrong.","triggerScenarios":"Sending a request with mm_content_hashes=[h1, h2] but only one non-None image in image_data; or images whose URL is None (skipped) while hashes were still provided for them; also passing hashes when image_data is empty.","commonSituations":"Client code that precomputes hashes for all multimodal items including videos; None-valued ImageData placeholders; a bug in hash-list construction after filtering images; version drift between client and server on how many hash entries are expected.","solutions":["Make mm_content_hashes contain exactly one entry per non-None image (URL-bearing) item in image_data, in the same order.","Do not include hashes for video/audio items; only images are hashed positionally here.","Drop mm_content_hashes (send None) if per-image content hashing is not needed.","Filter out None/empty image entries before building the hash list."],"exampleFix":"# before\nreq.mm_content_hashes = [h_img, h_video]\nreq.image_data = [img]\n# after\nreq.mm_content_hashes = [h_img]\nreq.image_data = [img]","handlingStrategy":"validation","validationCode":"def count_hashable_images(req) -> int:\n    n = 0\n    for item in req.image_data or []:\n        url = item.url if hasattr(item, \"url\") else (item or {}).get(\"url\") if isinstance(item, dict) else item\n        if item is not None and url is not None:\n            n += 1\n    return n\n\nif req.mm_content_hashes is not None:\n    assert len(req.mm_content_hashes) == count_hashable_images(req)","typeGuard":null,"tryCatchPattern":"try:\n    mm_data = build_mm_data(request_obj)\nexcept ValueError as e:\n    if \"mm_content_hashes\" in str(e):\n        request_obj.mm_content_hashes = None  # degrade to no content hashing\n        mm_data = build_mm_data(request_obj)","preventionTips":["Build mm_content_hashes with a list comprehension over the same filtered image list sent in image_data.","Never include video/audio hashes in mm_content_hashes.","Omit mm_content_hashes entirely (None) when content-hash caching is not required."],"tags":["multimodal","content-hash","request-validation","receiver"],"backgroundTag":"list-length-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}