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

  1. Inspect the `direct_url.json` and ensure exactly one of `vcs_info`/`archive_info`/`dir_info` is a non-null object
  2. Reinstall the package so pip writes a correct single-kind record
  3. 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

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


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