{"record":{"id":"0fef78e18fd342ec","repo":"pypa/pip","slug":"exactly-one-of-vcs-directory-archive-must-be-set","errorCode":null,"errorMessage":"Exactly one of vcs, directory, archive must be set if sdist and wheels are not set","messagePattern":"Exactly one of vcs, directory, archive must be set if sdist and wheels are not set","errorType":"validation","errorClass":"PylockValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/pylock.py","lineNumber":609,"sourceCode":"            vcs=_get_object(d, PackageVcs, \"vcs\"),\n            directory=_get_object(d, PackageDirectory, \"directory\"),\n            archive=_get_object(d, PackageArchive, \"archive\"),\n            index=_get(d, str, \"index\"),\n            sdist=_get_object(d, PackageSdist, \"sdist\"),\n            wheels=_get_sequence_of_objects(d, PackageWheel, \"wheels\"),\n            attestation_identities=_get_sequence(d, Mapping, \"attestation-identities\"),  # type: ignore[type-abstract]\n            tool=_get(d, Mapping, \"tool\"),  # type: ignore[type-abstract]\n        )\n        distributions = bool(package.sdist) + len(package.wheels or [])\n        direct_urls = (\n            bool(package.vcs) + bool(package.directory) + bool(package.archive)\n        )\n        if distributions > 0 and direct_urls > 0:\n            raise PylockValidationError(\n                \"None of vcs, directory, archive must be set if sdist or wheels are set\"\n            )\n        if distributions == 0 and direct_urls != 1:\n            raise PylockValidationError(\n                \"Exactly one of vcs, directory, archive must be set \"\n                \"if sdist and wheels are not set\"\n            )\n        for i, wheel in enumerate(package.wheels or []):\n            try:\n                (name, version, _, _) = parse_wheel_filename(wheel.filename)\n            except Exception as e:\n                raise PylockValidationError(\n                    f\"Invalid wheel filename {wheel.filename!r}\",\n                    context=f\"wheels[{i}]\",\n                ) from e\n            if name != package.name:\n                raise PylockValidationError(\n                    f\"Name in {wheel.filename!r} is not consistent with \"\n                    f\"package name {package.name!r}\",\n                    context=f\"wheels[{i}]\",\n                )\n            if package.version and version != package.version:","sourceCodeStart":591,"sourceCodeEnd":627,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/pylock.py#L591-L627","documentation":"Raised by Package._from_dict in pylock.py:608-612. When a package has no sdist and no wheels, it MUST declare exactly one direct-URL source among vcs/directory/archive. Zero sources (no way to fetch) or more than one (ambiguous) raises PylockValidationError.","triggerScenarios":"A [[packages]] entry with only name/index and no vcs, directory, archive, sdist or wheels; or a package declaring both vcs and directory (two sources).","commonSituations":"Forgetting the source entirely for a direct-reference package; providing two sources for redundancy; assuming 'index' counts as a source (it does not for this check).","solutions":["Add exactly one of vcs/directory/archive to the package entry.","If two are present, remove one so only a single source remains."],"exampleFix":"# before\n[[packages]]\nname = \"x\"\nversion = \"1.0\"\n# after\n[[packages]]\nname = \"x\"\nversion = \"1.0\"\n  [packages.vcs]\n  type = \"git\"\n  url = \"https://example.com/x.git\"\n  commit-id = \"abc123\"","handlingStrategy":"validation","validationCode":"def package_has_exactly_one_source(pkg) -> bool:\n    distributions = bool(pkg.get(\"sdist\")) + len(pkg.get(\"wheels\") or [])\n    direct = bool(pkg.get(\"vcs\")) + bool(pkg.get(\"directory\")) + bool(pkg.get(\"archive\"))\n    if distributions > 0:\n        return True\n    return direct == 1\n","typeGuard":null,"tryCatchPattern":"from packaging.pylock import Pylock, PylockValidationError\n\ntry:\n    Pylock.from_dict(d)\nexcept PylockValidationError as e:\n    ...\n","preventionTips":["Every direct-reference package must declare exactly one of vcs/directory/archive.","Remember 'index' is not a substitute for a direct source."],"tags":["pylock","validation","package-source"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}