{"record":{"id":"6ae55ddfa485b44b","repo":"sgl-project/sglang","slug":"content-hash-mismatch-for-media-data-index-exp","errorCode":null,"errorMessage":"content hash mismatch for media_data[{index}]: expected {caller_hash}, got {snapshot.content_digest}","messagePattern":"content hash mismatch for media_data\\[(.+?)\\]: expected (.+?), got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/media_artifacts/base.py","lineNumber":336,"sourceCode":"                    allow_featureless=allow_featureless,\n                )\n                if artifact is not None:\n                    artifacts[index] = artifact\n                    continue\n            load_indices.append(index)\n\n        # 2. read cache: build artifact key from media snapshot then try reading cache\n        snapshot_futures = {\n            index: self.io_executor.submit(\n                self.snapshot_media_source, media_data[index], modality\n            )\n            for index in load_indices\n        }\n        for index, future in snapshot_futures.items():\n            snapshot = await asyncio.wrap_future(future)\n            caller_hash = content_hashes[index]\n            if caller_hash is not None and caller_hash != snapshot.content_digest:\n                raise ValueError(\n                    f\"content hash mismatch for media_data[{index}]: \"\n                    f\"expected {caller_hash}, got {snapshot.content_digest}\"\n                )\n            snapshots[index] = snapshot\n            key = self._artifact_key(\n                snapshot.content_digest, media_data[index], modality=modality\n            )\n            keys[index] = key\n            artifacts[index] = self._get_cached_artifact(\n                key,\n                snapshot.content_digest,\n                modality,\n                allow_featureless=featureless_hit_mask[index],\n            )\n\n        # 3. deduplicate misses before decode\n        first_index_by_key: dict[str, int] = {}\n        previous_metadata: dict[str, MediaArtifact] = {}","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/media_artifacts/base.py#L318-L354","documentation":"After asynchronously loading a media snapshot, prepare_media_artifacts compares the snapshot's content_digest with the caller-supplied hash for that index. A mismatch means the underlying media content changed (or the hash was computed over different bytes), so the cached artifact would be stale/incorrect and the run is aborted.","triggerScenarios":"Supplying mm_content_hashes[i] that doesn't equal the digest of the actually loaded media_data[i] — e.g. a URL whose content was updated, a mutated local file, or a hash computed with a different algorithm/normalization.","commonSituations":"Mutable object-store URLs behind a CDN; hashing the prompt text instead of image bytes; using a stale hash cached from a previous version of the asset; cross-process hash algorithm drift (e.g. blake2 vs sha256).","solutions":["Recompute the content hash from the exact bytes being loaded and pass the fresh value","If the media is legitimately updated, treat it as a new item: new hash, new cache entry","Verify both sides use the same digest function (parse_content_hash's scheme) and same byte payload (no re-encoding)"],"exampleFix":"// before\nhashes = [old_hash_for_url]  # url content changed since\nawait prepare_media_artifacts(media, content_hashes=hashes, ...)\n// after\nhashes = [sha256(fetch_bytes(url)).hexdigest() for url in media]\nawait prepare_media_artifacts(media, content_hashes=hashes, ...)","handlingStrategy":"try-catch","validationCode":"digest = sha256(load_bytes(item)).hexdigest()\n# pass digest as the content hash so it matches the snapshot by construction","typeGuard":null,"tryCatchPattern":"try:\n    await prepare_media_artifacts(media, content_hashes=hashes, ...)\nexcept ValueError as e:\n    if \"content hash mismatch\" in str(e):\n        hashes = [compute_hash(m) for m in media]  # refresh and retry once\n        await prepare_media_artifacts(media, content_hashes=hashes, ...)\n    else:\n        raise","preventionTips":["Compute hashes from the exact bytes served, at request time","Use immutable/content-addressed URLs for media assets"],"tags":["multimodal","artifact-cache","content-hash","cache-invalidation"],"backgroundTag":"content-hash-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}