pypa/pip · critical · HashMismatch
THESE PACKAGES DO NOT MATCH THE HASHES FROM THE…
Error message
THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
What it means
Raised as HashMismatch by Hashes._raise (hashes.py:93) when none of the computed digests of a downloaded archive match any of the expected digests listed in the requirements file. This is pip's tamper-detection mechanism for --require-hashes mode: if even one file's hash differs from what was pinned, pip aborts to prevent installing potentially compromised or altered packages. The exception carries both the expected (allowed) and computed (gotten) hashes for diagnostics.
Solutions
- If you intentionally changed package versions, regenerate all hashes: delete the hashes, run `pip install --require-hashes -r requirements.txt`, and pip will print the correct hashes in the HashMissing error to add.
- Use `pip hash <downloaded-file>` to compute the correct sha256 for the exact file being installed.
- Ensure your index URL / mirror is serving the correct, unmodified files.
- Verify you are not mixing platform-specific wheels with hashes computed from a different platform's wheel.
Example fix
// before pkg==1.0 --hash=sha256:aaaa... (actual download hashes to bbbb...) // after pkg==1.0 --hash=sha256:bbbb... (run: pip hash pkg-1.0-py3-none-any.whl to get bbbb)
Defensive patterns
Strategy: try-catch
Validate before calling
import hashlib
def verify_archive_hash(filepath: str, expected_sha256: str) -> bool:
"""Pre-verify a downloaded file's sha256 before handing to pip."""
h = hashlib.sha256()
with open(filepath, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
h.update(chunk)
return h.hexdigest() == expected_sha256.lower() Try / catch
from pip._internal.exceptions import HashMismatch
try:
# pip install / download operation
pass
except HashMismatch as e:
# e has .allowed and .gotten attributes for diagnostics
print(f'Hash mismatch: expected {e.allowed}, got {e.gotten}')
# Re-download, regenerate lock file, or alert on potential tampering Prevention
- Regenerate all hashes whenever you change package versions in a lock file.
- Use pip-tools (`pip-compile --generate-hashes`) to keep hashes in sync automatically.
- Pin exact versions and verify your index/mirror serves correct files.
- Treat a hash mismatch on an unmodified lock as a potential security incident.
When it happens
Trigger: check_against_chunks / check_against_file is called after downloading an archive, iterates all expected hash algorithms, computes the actual digest, and if none match any entry in self._allowed[hash_name] for any algorithm, calls _raise at line 90. This happens during install with --require-hashes or when any requirement has a hash.
Common situations: Updating a package version in requirements.txt without regenerating its hash. A requirements file whose hashes were generated for a different file (different platform wheel vs sdist). Man-in-the-middle attack or compromised mirror serving a tampered package. Transitive dependency pinning changes that shift which file is downloaded.
Related errors
- Algorithm used in hash field has different value in hashes…
- Can't verify hashes for these file:// requirements because…
- Can't verify hashes for these requirements because we don't…
- Hashes are required in --require-hashes mode, but they are…
- In --require-hashes mode, all requirements must have their…
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/563e730230e25926.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_internal/utils/hashes.py:93
"""
gots = {}
for hash_name in self._allowed.keys():
try:
gots[hash_name] = hashlib.new(hash_name)
except (ValueError, TypeError):
raise InstallationError(f"Unknown hash name: {hash_name}")
for chunk in chunks:
for hash in gots.values():
hash.update(chunk)
for hash_name, got in gots.items():
if got.hexdigest() in self._allowed[hash_name]:
return
self._raise(gots)
def _raise(self, gots: dict[str, _Hash]) -> NoReturn:
raise HashMismatch(self._allowed, gots)
def check_against_file(self, file: BinaryIO) -> None:
"""Check good hashes against a file-like object
Raise HashMismatch if none match.
"""
return self.check_against_chunks(read_chunks(file))
def check_against_path(self, path: str) -> None:
with open(path, "rb") as file:
return self.check_against_file(file)
def has_one_of(self, hashes: Mapping[str, str]) -> bool:
"""Return whether any of the given hashes are allowed."""
for hash_name, hex_digest in hashes.items():
if self.is_hash_allowed(hash_name, hex_digest):
return TrueView on GitHub (pinned to f399c37189)