{"record":{"id":"55b61db548b01da6","repo":"sgl-project/sglang","slug":"mm-content-hashes-has-len-content-hashes-entrie","errorCode":null,"errorMessage":"mm_content_hashes has {len(content_hashes)} entries for {media_count} {modality.name.lower()} items","messagePattern":"mm_content_hashes has (.+?) entries for (.+?) (.+?) items","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/media_artifacts/base.py","lineNumber":285,"sourceCode":"        \"\"\"Try resolving one preprocess-cache artifact for each processor input.\n\n        Each media input is looked up independently, and results preserve the\n        input order. A cache hit returns the stored artifact (the cache item).\n        A miss snapshots and decodes the raw input, runs\n        ``prepare_artifact_batch``, stores its cache-safe artifact, and returns\n        the prepared artifact to the current request. Duplicate and concurrent\n        misses share the same preprocessing work.\n\n        This stage is prompt-independent. It does not create prompt tokens,\n        offsets, or ``MultimodalDataItem`` objects; the model processor uses the\n        returned artifacts to compose those request-specific values afterward.\n        \"\"\"\n        modality = self._resolve_artifact_modality(modality)\n        media_count = len(media_data)\n        if content_hashes is None:\n            content_hashes = [None] * media_count\n        if len(content_hashes) != media_count:\n            raise ValueError(\n                f\"mm_content_hashes has {len(content_hashes)} entries for \"\n                f\"{media_count} {modality.name.lower()} items\"\n            )\n        content_hashes = [parse_content_hash(value) for value in content_hashes]\n\n        if featureless_hit_mask is None:\n            featureless_hit_mask = [False] * media_count\n        if len(featureless_hit_mask) != media_count:\n            raise ValueError(\"featureless_hit_mask must align with media_data\")\n\n        # keep per-input state aligned for duplicates and partial hits\n        artifacts: list[Optional[MediaArtifact]] = [None] * media_count\n        snapshots: list[Optional[MediaSnapshot]] = [None] * media_count\n        keys: list[Optional[str]] = [None] * media_count\n\n        # 1. fast path: resolve trusted provided hash hits without reading media\n        # e.g., an image could be submitted with a provided hash:\n        # \"image_url\": {","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/media_artifacts/base.py#L267-L303","documentation":"prepare_media_artifacts requires the optional mm_content_hashes list to be exactly as long as media_data; a length mismatch means the caller's per-item content hashes cannot be aligned with the media items, so the artifact cache cannot be keyed safely.","triggerScenarios":"Calling prepare_media_artifacts (via process_mm_data_async) with content_hashes shorter/longer than media_data — e.g. hashing only new items while resending the full media list, or vice versa.","commonSituations":"Incremental prefetch flows that compute hashes for a subset of images; EPD/speculative decoding paths where the hash list is built per-request but media_data is per-batch; races where media list is mutated between hash computation and the call.","solutions":["Regenerate content_hashes with one entry per item in the current media_data list (in the same order)","If you don't have hashes, pass content_hashes=None instead of a partial list","Log len(media_data) and len(content_hashes) upstream to find where they diverge"],"exampleFix":"// before\nhashes = [h for h, m in zip(prev_hashes, media) if m in new_set]  # shorter than media\nawait prepare_media_artifacts(media, content_hashes=hashes, ...)\n// after\nhashes = [compute_hash(m) for m in media]\nawait prepare_media_artifacts(media, content_hashes=hashes, ...)","handlingStrategy":"validation","validationCode":"assert content_hashes is None or len(content_hashes) == len(media_data), (len(content_hashes), len(media_data))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always derive hashes with a list comprehension over the exact media_data list being passed","Never reuse a hash list from a previous/different media list"],"tags":["multimodal","artifact-cache","kimi-k3","input-validation"],"backgroundTag":"list-length-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}