pypa/pip · error · HashUnpinned
In --require-hashes mode, all requirements must have their v
Error message
In --require-hashes mode, all requirements must have their versions pinned with ==. These do not:
What it means
Raised as HashUnpinned when --require-hashes is active and a (non-direct) requirement is not pinned with an exact '==' specifier. At prepare.py:483-484, _get_linked_req_hashes checks 'not req.is_direct and not req.is_pinned' and aborts, because a floating version would cause a future hash mismatch when a new version is uploaded. This is a reproducibility guard, not strictly a security check.
Source
Thrown at src/pip/_internal/operations/prepare.py:484
if not self.require_hashes:
return req.hashes(trust_internet=True)
# We could check these first 2 conditions inside unpack_url
# and save repetition of conditions, but then we would
# report less-useful error messages for unhashable
# requirements, complaining that there's no hash provided.
if req.link.is_vcs:
raise VcsHashUnsupported()
if req.link.is_existing_dir():
raise DirectoryUrlHashUnsupported()
# Unpinned packages are asking for trouble when a new version
# is uploaded. This isn't a security check, but it saves users
# a surprising hash mismatch in the future.
# file:/// URLs aren't pinnable, so don't complain about them
# not being pinned.
if not req.is_direct and not req.is_pinned:
raise HashUnpinned()
# If known-good hashes are missing for this requirement,
# shim it with a facade object that will provoke hash
# computation and then raise a HashMissing exception
# showing the user what the hash should be.
return req.hashes(trust_internet=False) or MissingHashes()
def _fetch_metadata_only(
self,
req: InstallRequirement,
) -> BaseDistribution | None:
if self.legacy_resolver:
logger.debug(
"Metadata-only fetching is not used in the legacy resolver",
)
return None
if self.require_hashes:
logger.debug(View on GitHub (pinned to d7d0d0a394)
Solutions
- Pin every requirement to an exact version with '==' in the requirements file.
- Regenerate hashes for the pinned set: pip install --require-hashes -r requirements.txt (pip will report the expected hashes), or use pip-compile / pip freeze to produce pinned hashes.
- Ensure no requirement uses '>=', '~=', '<', '*', or bare names when --require-hashes is on.
- For transitive deps, pin them explicitly in the (hashed) requirements file.
Example fix
# before (requirements.txt) --require-hashes requests >=2.0 # after --require-hashes requests==2.31.0 \ --hash=sha256:aaaa... \ --hash=sha256:bbbb...
Defensive patterns
Strategy: validation
Validate before calling
from pip._vendor.packaging.requirements import Requirement
def assert_all_pinned(requirements_text):
unpinned = []
for line in requirements_text.splitlines():
line = line.split("#", 1)[0].strip()
if not line or line.startswith("-"):
continue
try:
req = Requirement(line)
except Exception:
continue
specs = str(req.specifier)
if "==" not in specs or "*" in specs or "~=" in specs or ">" in specs or "<" in specs.replace("==",""):
unpinned.append(line)
if unpinned:
raise SystemExit(f"unpinned under --require-hashes: {unpinned}") Type guard
from pip._vendor.packaging.requirements import Requirement
def is_exact_pinned(line: str) -> bool:
try:
req = Requirement(line)
except Exception:
return False
return any(op == "==" for op, _ in req.specifier) Prevention
- Generate hashed requirements with pip-compile or pip freeze, never hand-edit floating specs.
- Run a pre-commit lint that rejects unpinned requirements when hashes are required.
- Keep transitive dependencies pinned in the hashed lockfile.
When it happens
Trigger: A requirements file used with --require-hashes contains a requirement like 'requests>=2.0' or 'requests' (no specifier, or a range/compatible-release) instead of 'requests==2.31.0'.
Common situations: Generating a hashed requirements.txt via pip freeze without pinning, or mixing a constraints file that leaves versions floating. Common when adopting --require-hashes on an existing unpinned requirements file.
Related errors
- Can't verify hashes for these requirements because we don't
- Can't verify hashes for these file:// requirements because t
- The editable requirement {req} cannot be installed when requ
- Unknown hash name: {hash_name}
- THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/d93995f322538bd7.json.
Report an issue: GitHub.