pypa/pip · error · DirectUrlValidationError
Algorithm used in hash field is not present in hashes field
Error message
Algorithm {hash_algorithm!r} used in hash field is not present in hashes field What it means
When both the legacy hash field and the newer hashes mapping are present in archive_info, they must be consistent. The algorithm extracted from the legacy hash (the part before =) must also be a key in the hashes mapping. If it is missing, DirectUrlValidationError is raised because the two hash representations disagree about which algorithms are available.
Solutions
- Ensure the algorithm in the legacy hash matches one of the keys in hashes
- Remove the legacy hash field and rely solely on hashes
- Regenerate the metadata from the correct source using to_dict()
Example fix
# before
data = {
"url": "https://example.com/pkg.tar.gz",
"archive_info": {
"hash": "md5=abc123",
"hashes": {"sha256": "def456"} # md5 not in hashes
}
}
# after
data = {
"url": "https://example.com/pkg.tar.gz",
"archive_info": {"hashes": {"sha256": "def456"}} # drop legacy hash
} Defensive patterns
Strategy: validation
Validate before calling
def validate_hash_consistency(archive_info: dict) -> None:
legacy = archive_info.get("hash")
hashes = archive_info.get("hashes")
if legacy and hashes:
algo = legacy.split("=", 1)[0]
if algo not in hashes:
raise ValueError(f"Algorithm {algo!r} from hash not in hashes") Type guard
def are_hashes_consistent(archive_info: dict) -> bool:
legacy = archive_info.get("hash")
hashes = archive_info.get("hashes")
if not legacy or not hashes:
return True
return legacy.split("=", 1)[0] in hashes Try / catch
from packaging.direct_url import DirectUrl, DirectUrlValidationError
try:
du = DirectUrl.from_dict(data)
except DirectUrlValidationError as e:
if "not present in hashes" in str(e):
data["archive_info"].pop("hash", None)
du = DirectUrl.from_dict(data) Prevention
- Do not mix legacy hash and hashes fields
- Regenerate metadata from a single source of truth
- Validate cross-field consistency before parsing
When it happens
Trigger: An archive_info block with both hash and hashes where the algorithm in hash does not appear in hashes: e.g. {'hash': 'md5=abc', 'hashes': {'sha256': 'def'}}.
Common situations: Metadata generated by tools that write both fields with different algorithms. Migration artifacts where the legacy field was not updated alongside the new field.
Related errors
- Algorithm used in hash field has different value in hashes…
- Hash values must be strings
- Invalid hash format (expected ' = ')
- Exactly one of vcs_info, archive_info, dir_info must be…
- File URL must be absolute when dir_info is present
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/7cbfa0df22038088.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/direct_url.py:218
if hashes is not None and not all(isinstance(h, str) for h in hashes.values()):
raise DirectUrlValidationError(
"Hash values must be strings", context="hashes"
)
legacy_hash = _get(d, str, "hash")
if legacy_hash is not None:
if "=" not in legacy_hash:
raise DirectUrlValidationError(
"Invalid hash format (expected '<algorithm>=<hash>')",
context="hash",
)
hash_algorithm, hash_value = legacy_hash.split("=", 1)
if hashes is None:
# if `hashes` are not present, we can derive it from the legacy `hash`
hashes = {hash_algorithm: hash_value}
else:
# if `hashes` are present, the legacy `hash` must match one of them
if hash_algorithm not in hashes:
raise DirectUrlValidationError(
f"Algorithm {hash_algorithm!r} used in hash field "
f"is not present in hashes field",
context="hashes",
)
if hashes[hash_algorithm] != hash_value:
raise DirectUrlValidationError(
f"Algorithm {hash_algorithm!r} used in hash field "
f"has different value in hashes field",
context="hash",
)
return cls(hashes=hashes)
@dataclasses.dataclass(frozen=True, init=False)
class DirInfo:
"""The local directory information of a :class:`DirectUrl`."""
editable: bool | None = NoneView on GitHub (pinned to f399c37189)