pypa/pip · error · DirectUrlValidationError
Exactly one of vcs_info, archive_info, dir_info must be pres
Error message
Exactly one of vcs_info, archive_info, dir_info must be present
What it means
Raised by `DirectUrl._from_dict` when the number of info blocks among `vcs_info`, `archive_info`, and `dir_info` is not exactly one. PEP 610 requires a direct URL to identify precisely one source kind: a VCS checkout, an archive (sdist/wheel), or a local directory. Zero or more-than-one is a schema violation.
Source
Thrown at src/pip/_vendor/packaging/direct_url.py:277
object.__setattr__(self, "vcs_info", vcs_info)
object.__setattr__(self, "dir_info", dir_info)
object.__setattr__(self, "subdirectory", subdirectory)
@classmethod
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
direct_url = cls(
url=_get_required(d, str, "url"),
archive_info=_get_object(d, ArchiveInfo, "archive_info"),
vcs_info=_get_object(d, VcsInfo, "vcs_info"),
dir_info=_get_object(d, DirInfo, "dir_info"),
subdirectory=_get(d, str, "subdirectory"),
)
if (
bool(direct_url.vcs_info)
+ bool(direct_url.archive_info)
+ bool(direct_url.dir_info)
) != 1:
raise DirectUrlValidationError(
"Exactly one of vcs_info, archive_info, dir_info must be present"
)
if direct_url.dir_info is not None and not direct_url.url.startswith("file://"):
raise DirectUrlValidationError(
"URL scheme must be file:// when dir_info is present",
context="url",
)
# XXX subdirectory must be relative, can we, should we validate that here?
return direct_url
@classmethod
def from_dict(cls, d: Mapping[str, Any], /) -> Self:
"""Create and validate a DirectUrl instance from a JSON dictionary."""
return cls._from_dict(d)
def to_dict(
self,
*,View on GitHub (pinned to d7d0d0a394)
Solutions
- Inspect the `direct_url.json` and ensure exactly one of `vcs_info`/`archive_info`/`dir_info` is a non-null object
- Reinstall the package so pip writes a correct single-kind record
- Validate the dict shape: `sum(k in d and d[k] for k in (...)) == 1` before `from_dict`
Example fix
// before
{'url': 'https://x/pkg.tar.gz'} # no info block
// after
{'url': 'https://x/pkg.tar.gz', 'archive_info': {}} Defensive patterns
Strategy: validation
Validate before calling
def validate_direct_url_kind(d: dict) -> None:
present = [k for k in ('vcs_info','archive_info','dir_info') if d.get(k)]
if len(present) != 1:
raise ValueError(f'exactly one info block required, found {present}') Type guard
def has_exactly_one_info_block(d: object) -> bool:
if not isinstance(d, dict):
return False
return sum(bool(d.get(k)) for k in ('vcs_info','archive_info','dir_info')) == 1 Try / catch
from packaging.direct_url import DirectUrl, DirectUrlValidationError
try:
du = DirectUrl.from_dict(d)
except DirectUrlValidationError as e:
if 'Exactly one of' in str(e):
raise ValueError(f'direct_url.json malformed, reinstall package') from e
raise Prevention
- Let pip be the sole writer of direct_url.json
- Validate the info-block count before constructing DirectUrl
- Audit custom installers for PEP 610 compliance
When it happens
Trigger: A `direct_url.json` with no info block at all (just `url`), or with both `vcs_info` and `archive_info`, or all three present. Also triggered by passing an empty dict or omitting all info keys.
Common situations: A custom installer that wrote only `url` and forgot the info block; a hand-merged record combining VCS and archive metadata; an editable install whose record was partially deleted; tooling that emits `null` for all three blocks.
Related errors
- Unexpected type {type(value).__name__} (expected {expected_t
- Hash values must be strings
- Invalid hash format (expected '<algorithm>=<hash>')
- Algorithm {hash_algorithm!r} used in hash field is not prese
- Algorithm {hash_algorithm!r} used in hash field has differen
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/d775e8e0a8a454eb.json.
Report an issue: GitHub.