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

  1. Make the legacy `hash` reference an algorithm that exists in `hashes`
  2. Drop the redundant legacy `hash` field entirely (modern `hashes` is sufficient)
  3. 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

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


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/7cbfa0df22038088.json. Report an issue: GitHub.