{"record":{"id":"2caffe286d0817d1","repo":"pypa/pip","slug":"none-of-vcs-directory-archive-must-be-set-if-sdi","errorCode":null,"errorMessage":"None of vcs, directory, archive must be set if sdist or wheels are set","messagePattern":"None of vcs, directory, archive must be set if sdist or wheels are set","errorType":"validation","errorClass":"PylockValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/pylock.py","lineNumber":605,"sourceCode":"            version=_get_as(d, str, Version, \"version\"),\n            requires_python=_get_as(d, str, SpecifierSet, \"requires-python\"),\n            dependencies=_get_sequence(d, Mapping, \"dependencies\"),  # type: ignore[type-abstract]\n            marker=_get_as(d, str, Marker, \"marker\"),\n            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 \"","sourceCodeStart":587,"sourceCodeEnd":623,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/pylock.py#L587-L623","documentation":"Raised by Package._from_dict in pylock.py:604-607. A package is EITHER a set of built distributions (sdist and/or wheels) OR a single direct-URL source (vcs/directory/archive), never both. If at least one distribution and at least one direct-url source are present together, PylockValidationError is raised.","triggerScenarios":"A [[packages]] entry with both wheels = [...] and directory = {...}; a package with both sdist and vcs; any combination where distributions > 0 and direct_urls > 0.","commonSituations":"Mixing a built artifact with a source location in one package entry; a merge/union tool combining two package records into one.","solutions":["Split into separate package entries (one for the built distribution, one for the source), or","Drop the vcs/directory/archive fields so only sdist/wheels remain (or vice-versa)."],"exampleFix":"# before\n[[packages]]\nname = \"x\"\nwheels = [{ name = \"x-1.0-py3-none-any.whl\", hashes = {...} }]\n  [packages.directory]\n  path = \"./x\"\n# after\n[[packages]]\nname = \"x\"\nwheels = [{ name = \"x-1.0-py3-none-any.whl\", hashes = {...} }]","handlingStrategy":"validation","validationCode":"def package_source_consistent(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    return not (distributions > 0 and direct > 0)\n","typeGuard":null,"tryCatchPattern":"from packaging.pylock import Pylock, PylockValidationError\n\ntry:\n    Pylock.from_dict(d)\nexcept PylockValidationError as e:\n    ...\n","preventionTips":["Keep each package entry as either built distributions (sdist/wheels) or a single direct URL source, not both.","Validate at the generator boundary before writing the lock file."],"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"}