pypa/pip · error · DirectUrlValidationError
Invalid hash format (expected '<algorithm>=<hash>')
Error message
Invalid hash format (expected '<algorithm>=<hash>')
What it means
Raised by `ArchiveInfo._from_dict` when the legacy `hash` field is present but does not contain an `=` separator. PEP 610's legacy `hash` field must follow the form `<algorithm>=<hexdigest>` (e.g. `sha256=abc123...`). Without the separator, the algorithm and digest cannot be split.
Source
Thrown at src/pip/_vendor/packaging/direct_url.py:195
def __init__(
self,
*,
hashes: Mapping[str, str] | None = None,
) -> None:
object.__setattr__(self, "hashes", hashes)
@classmethod
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
hashes = _get(d, Mapping, "hashes") # type: ignore[type-abstract]
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 "View on GitHub (pinned to d7d0d0a394)
Solutions
- Reformat the value as `algorithm=digest`, e.g. `sha256=<hexdigest>`
- Prefer the modern `hashes` mapping (`{"sha256": "<hexdigest>"}`) over the legacy single `hash` field
- Reinstall the package to regenerate a correct record
Example fix
// before
{'archive_info': {'hash': 'abc123def456'}}
// after
{'archive_info': {'hash': 'sha256=abc123def456'}} Defensive patterns
Strategy: validation
Validate before calling
def format_legacy_hash(algorithm: str, digest: str) -> str:
if '=' in algorithm or '=' in digest:
raise ValueError('algorithm/digest must not contain =')
return f'{algorithm}={digest}' Type guard
import re
def is_valid_legacy_hash(s: object) -> bool:
return isinstance(s, str) and bool(re.match(r'^[A-Za-z0-9_-]+=[0-9a-fA-F]+$', s)) Try / catch
from packaging.direct_url import ArchiveInfo, DirectUrlValidationError
try:
ArchiveInfo._from_dict(d)
except DirectUrlValidationError as e:
if 'Invalid hash format' in str(e):
h = d['archive_info'].pop('hash')
d['archive_info']['hash'] = f'sha256={h}' # repair if algorithm known
raise Prevention
- Always write hashes as 'algorithm=digest'
- Prefer the modern hashes mapping over the legacy hash field
- Validate legacy hash strings with an '=' check before persisting
When it happens
Trigger: A `direct_url.json` with `archive_info.hash` set to a bare hex digest like `"abc123def456"` or `"sha256:abc123"` (colon instead of equals).
Common situations: Older tooling that wrote the digest without the algorithm prefix; copy-paste from a `--hash` line that used a different separator; hand-crafted install records.
Related errors
- Hash values must be strings
- Algorithm {hash_algorithm!r} used in hash field is not prese
- 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/55c70406a7bd8c92.json.
Report an issue: GitHub.