{"record":{"id":"51c5a74ded664904","repo":"Comfy-Org/ComfyUI","slug":"hash-mismatch","errorCode":"HASH_MISMATCH","errorMessage":"Uploaded file hash does not match provided hash.","messagePattern":"Uploaded file hash does not match provided hash\\.","errorType":"http","errorClass":"HashMismatchError","httpStatus":400,"severity":"error","filePath":"app/assets/services/ingest.py","lineNumber":488,"sourceCode":"    name: str | None = None,\n    tags: list[str] | None = None,\n    user_metadata: dict | None = None,\n    client_filename: str | None = None,\n    owner_id: str = \"\",\n    expected_hash: str | None = None,\n    mime_type: str | None = None,\n    preview_id: str | None = None,\n) -> UploadResult:\n    try:\n        digest, _ = hashing.compute_blake3_hash(temp_path)\n    except ImportError as e:\n        raise DependencyMissingError(str(e))\n    except Exception as e:\n        raise RuntimeError(f\"failed to hash uploaded file: {e}\")\n    asset_hash = \"blake3:\" + digest\n\n    if expected_hash and asset_hash != expected_hash.strip().lower():\n        raise HashMismatchError(\"Uploaded file hash does not match provided hash.\")\n\n    with create_session() as session:\n        existing = get_asset_by_hash(session, asset_hash=asset_hash)\n\n    if existing is not None:\n        # Once content is already known, duplicate byte uploads are treated as\n        # reference-only creation. Request tags are labels only here: do not\n        # require upload destination tags, do not move bytes, and do not\n        # synthesize path-derived classification or uploaded provenance.\n        with contextlib.suppress(Exception):\n            if temp_path and os.path.exists(temp_path):\n                os.remove(temp_path)\n\n        display_name = _sanitize_filename(name or client_filename, fallback=digest)\n        result = _register_existing_asset(\n            asset_hash=asset_hash,\n            name=display_name,\n            user_metadata=user_metadata or {},","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/app/assets/services/ingest.py#L470-L506","documentation":"Raised by the asset upload pipeline when the BLAKE3 hash computed over the received temp file ('blake3:<digest>') does not equal the client-declared expected_hash (after strip+lowercase normalization). This is end-to-end integrity verification: the bytes that landed on disk differ from the bytes the sender hashed, so the upload is refused with HASH_MISMATCH instead of being recorded under a false hash.","triggerScenarios":"POST an asset upload with an `expected_hash` header/field of 'blake3:abc...' while the multipart body contains different bytes — truncated transfer, a mutated file between hashing and sending, wrong hash algorithm (e.g. SHA-256 hex passed as blake3), or an uppercase/unprefixed hash string that doesn't match after normalization.","commonSituations":"Client hashes with sha256 instead of blake3; file modified (or re-saved by an editor/cloud sync) between hashing and upload; partial upload due to connection reset; hash copied with whitespace or missing 'blake3:' prefix; case mismatch in the digest.","solutions":["Re-hash the exact file bytes with BLAKE3 immediately before upload and send 'blake3:' + lowercase hex digest.","Verify the file wasn't modified after hashing (disable sync/editors; hash and upload in one step).","Retry the upload on a clean connection; a truncated body will never match.","Omit expected_hash entirely if you don't need integrity verification — the server computes its own hash regardless."],"exampleFix":"# before\nexpected = 'sha256:' + hashlib.sha256(data).hexdigest()\n\n# after\nimport blake3\nexpected = 'blake3:' + blake3.blake3(file_bytes).hexdigest()","handlingStrategy":"validation","validationCode":"import blake3\n\ndef make_expected_hash(path) -> str:\n    h = blake3.blake3()\n    with open(path, 'rb') as f:\n        for chunk in iter(lambda: f.read(1 << 20), b''):\n            h.update(chunk)\n    return 'blake3:' + h.hexdigest()","typeGuard":"import re\nEXPECTED_HASH_RE = re.compile(r'^blake3:[0-9a-f]{64}$')\ndef is_valid_expected_hash(v) -> bool:\n    return bool(v) and EXPECTED_HASH_RE.match(v.strip().lower()) is not None","tryCatchPattern":"try:\n    upload(path, expected_hash=make_expected_hash(path))\nexcept HashMismatchError:\n    # re-hash and retry once; persistent mismatch means local file changed\n    upload(path, expected_hash=make_expected_hash(path))","preventionTips":["Hash with BLAKE3 and prefix 'blake3:', lowercase hex, no whitespace","Hash immediately before upload from the exact file being sent","Validate the hash format client-side before the request","Skip expected_hash when integrity checking isn't needed"],"tags":["upload","hash","integrity","blake3"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}