{"id":"d775e8e0a8a454eb","repo":"pypa/pip","slug":"exactly-one-of-vcs-info-archive-info-dir-info-mu","errorCode":null,"errorMessage":"Exactly one of vcs_info, archive_info, dir_info must be present","messagePattern":"Exactly one of vcs_info, archive_info, dir_info must be present","errorType":"validation","errorClass":"DirectUrlValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/direct_url.py","lineNumber":277,"sourceCode":"        object.__setattr__(self, \"vcs_info\", vcs_info)\n        object.__setattr__(self, \"dir_info\", dir_info)\n        object.__setattr__(self, \"subdirectory\", subdirectory)\n\n    @classmethod\n    def _from_dict(cls, d: Mapping[str, Any]) -> Self:\n        direct_url = cls(\n            url=_get_required(d, str, \"url\"),\n            archive_info=_get_object(d, ArchiveInfo, \"archive_info\"),\n            vcs_info=_get_object(d, VcsInfo, \"vcs_info\"),\n            dir_info=_get_object(d, DirInfo, \"dir_info\"),\n            subdirectory=_get(d, str, \"subdirectory\"),\n        )\n        if (\n            bool(direct_url.vcs_info)\n            + bool(direct_url.archive_info)\n            + bool(direct_url.dir_info)\n        ) != 1:\n            raise DirectUrlValidationError(\n                \"Exactly one of vcs_info, archive_info, dir_info must be present\"\n            )\n        if direct_url.dir_info is not None and not direct_url.url.startswith(\"file://\"):\n            raise DirectUrlValidationError(\n                \"URL scheme must be file:// when dir_info is present\",\n                context=\"url\",\n            )\n        # XXX subdirectory must be relative, can we, should we validate that here?\n        return direct_url\n\n    @classmethod\n    def from_dict(cls, d: Mapping[str, Any], /) -> Self:\n        \"\"\"Create and validate a DirectUrl instance from a JSON dictionary.\"\"\"\n        return cls._from_dict(d)\n\n    def to_dict(\n        self,\n        *,","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/direct_url.py#L259-L295","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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`"],"exampleFix":"// before\n{'url': 'https://x/pkg.tar.gz'}  # no info block\n// after\n{'url': 'https://x/pkg.tar.gz', 'archive_info': {}}","handlingStrategy":"validation","validationCode":"def validate_direct_url_kind(d: dict) -> None:\n    present = [k for k in ('vcs_info','archive_info','dir_info') if d.get(k)]\n    if len(present) != 1:\n        raise ValueError(f'exactly one info block required, found {present}')","typeGuard":"def has_exactly_one_info_block(d: object) -> bool:\n    if not isinstance(d, dict):\n        return False\n    return sum(bool(d.get(k)) for k in ('vcs_info','archive_info','dir_info')) == 1","tryCatchPattern":"from packaging.direct_url import DirectUrl, DirectUrlValidationError\ntry:\n    du = DirectUrl.from_dict(d)\nexcept DirectUrlValidationError as e:\n    if 'Exactly one of' in str(e):\n        raise ValueError(f'direct_url.json malformed, reinstall package') from e\n    raise","preventionTips":["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"],"tags":["packaging","direct-url","pep610","schema"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}