pypa/pip · error · DirectUrlValidationError
Algorithm {hash_algorithm!r} used in hash field is not prese
Error message
Algorithm {hash_algorithm!r} used in hash field is not present in hashes field What it means
Raised by `ArchiveInfo._from_dict` when both the modern `hashes` mapping and the legacy `hash` string are present, but the algorithm named in the legacy `hash` (e.g. `md5=...`) does not appear as a key in `hashes`. The legacy field must be consistent with `hashes` — pip uses this cross-check to detect tampering or schema drift.
Source
Thrown at src/pip/_vendor/packaging/direct_url.py:206
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:
editable: bool | None = None
def __init__(View on GitHub (pinned to d7d0d0a394)
Solutions
- Make the legacy `hash` reference an algorithm that exists in `hashes`
- Drop the redundant legacy `hash` field entirely (modern `hashes` is sufficient)
- Reinstall the package to regenerate a consistent record
Example fix
// before
{'archive_info': {'hashes': {'sha256':'aaa'}, 'hash': 'md5=bbb'}}
// after
{'archive_info': {'hashes': {'sha256':'aaa'}, 'hash': 'sha256=aaa'}} Defensive patterns
Strategy: validation
Validate before calling
def reconcile_hashes(hashes: dict, legacy_hash: str) -> None:
algo, _ = legacy_hash.split('=', 1)
if hashes is not None and algo not in hashes:
raise ValueError(f'legacy hash algorithm {algo!r} missing from hashes') Type guard
def hashes_are_consistent(hashes: dict, legacy_hash: str) -> bool:
algo, _ = legacy_hash.split('=', 1)
return algo in hashes Try / catch
from packaging.direct_url import ArchiveInfo, DirectUrlValidationError
try:
ArchiveInfo._from_dict(d)
except DirectUrlValidationError as e:
if 'not present in hashes' in str(e):
d['archive_info'].pop('hash', None) # drop inconsistent legacy field
raise Prevention
- Use either hashes or legacy hash, not both, when generating records
- Keep hash fields in sync when mutating install metadata
- Reinstall to regenerate consistent records
When it happens
Trigger: A `direct_url.json` with `hashes: {"sha256": "aaa"}` and legacy `hash: "md5=bbb"` — the `md5` algorithm is referenced in `hash` but absent from `hashes`.
Common situations: A record partially rewritten by a tool that updated `hashes` but left a stale legacy `hash`; manual edits that added an extra algorithm; inconsistent metadata after a wheel was re-hashed.
Related errors
- Hash values must be strings
- Invalid hash format (expected '<algorithm>=<hash>')
- Algorithm {hash_algorithm!r} used in hash field has differen
- 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/7cbfa0df22038088.json.
Report an issue: GitHub.