pypa/pip · error · DirectUrlValidationError
Algorithm {hash_algorithm!r} used in hash field has differen
Error message
Algorithm {hash_algorithm!r} used in hash field has different value in hashes field What it means
Raised by `ArchiveInfo._from_dict` when both `hashes` and legacy `hash` are present, the algorithm matches a key in `hashes`, but the digest value differs. This signals that the two fields disagree about the package's content hash — treated as a potential tampering/corruption indicator.
Source
Thrown at src/pip/_vendor/packaging/direct_url.py:212
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:
editable: bool | None = None
def __init__(
self,
*,
editable: bool | None = None,
) -> None:
object.__setattr__(self, "editable", editable)
View on GitHub (pinned to d7d0d0a394)
Solutions
- Recompute the hash of the actual archive and update both fields consistently
- Remove the legacy `hash` field and keep only `hashes`
- Reinstall the package from a trusted index to regenerate clean metadata
Example fix
// before
{'archive_info': {'hashes': {'sha256':'aaa'}, 'hash': 'sha256=bbb'}}
// after
{'archive_info': {'hashes': {'sha256':'aaa'}, 'hash': 'sha256=aaa'}} Defensive patterns
Strategy: validation
Validate before calling
def verify_hash_consistency(hashes: dict, legacy_hash: str) -> None:
algo, value = legacy_hash.split('=', 1)
if hashes is not None and hashes.get(algo) != value:
raise ValueError(f'conflicting {algo} digest between hashes and legacy hash') Type guard
def hash_values_match(hashes: dict, legacy_hash: str) -> bool:
algo, value = legacy_hash.split('=', 1)
return hashes is None or hashes.get(algo) == value Try / catch
from packaging.direct_url import ArchiveInfo, DirectUrlValidationError
try:
ArchiveInfo._from_dict(d)
except DirectUrlValidationError as e:
if 'different value' in str(e):
log.error('hash mismatch — possible corruption, reinstalling')
d['archive_info'].pop('hash', None)
raise Prevention
- Treat hash conflicts as integrity violations — recompute from the artifact
- Single-source hashes into one field only
- Reinstall from a trusted index when conflicts surface
When it happens
Trigger: A record like `hashes: {"sha256": "aaa"}` and legacy `hash: "sha256=bbb"` — same algorithm, conflicting digests.
Common situations: A wheel was re-published with new content but the legacy `hash` field was not updated; partial edits to install metadata; a corrupted or man-in-the-middle tampered record.
Related errors
- Hash values must be strings
- Invalid hash format (expected '<algorithm>=<hash>')
- Algorithm {hash_algorithm!r} used in hash field is not prese
- Unexpected type {type(value).__name__} (expected {expected_t
- Exactly one of vcs_info, archive_info, dir_info must be pres
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/a4d2794b5a795b26.json.
Report an issue: GitHub.