pypa/pip · error · PylockValidationError
None of vcs, directory, archive must be set if sdist or whee
Error message
None of vcs, directory, archive must be set if sdist or wheels are set
What it means
Raised as PylockValidationError by Package._from_dict in packaging.pylock when a package entry specifies both a built distribution (sdist and/or wheels) and a direct source locator (vcs, directory, or archive). pylock treats these as mutually exclusive: a package is either pinned to artifacts or pinned to a source, not both.
Source
Thrown at src/pip/_vendor/packaging/pylock.py:589
version=_get_as(d, str, Version, "version"),
requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract]
marker=_get_as(d, str, Marker, "marker"),
vcs=_get_object(d, PackageVcs, "vcs"),
directory=_get_object(d, PackageDirectory, "directory"),
archive=_get_object(d, PackageArchive, "archive"),
index=_get(d, str, "index"),
sdist=_get_object(d, PackageSdist, "sdist"),
wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"),
attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract]
tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
)
distributions = bool(package.sdist) + len(package.wheels or [])
direct_urls = (
bool(package.vcs) + bool(package.directory) + bool(package.archive)
)
if distributions > 0 and direct_urls > 0:
raise PylockValidationError(
"None of vcs, directory, archive must be set if sdist or wheels are set"
)
if distributions == 0 and direct_urls != 1:
raise PylockValidationError(
"Exactly one of vcs, directory, archive must be set "
"if sdist and wheels are not set"
)
for i, wheel in enumerate(package.wheels or []):
try:
(name, version, _, _) = parse_wheel_filename(wheel.filename)
except Exception as e:
raise PylockValidationError(
f"Invalid wheel filename {wheel.filename!r}",
context=f"wheels[{i}]",
) from e
if name != package.name:
raise PylockValidationError(
f"Name in {wheel.filename!r} is not consistent with "View on GitHub (pinned to d7d0d0a394)
Solutions
- Decide whether the package is artifact-pinned or source-pinned, then keep only that side.
- Remove the vcs/directory/archive block when sdist/wheels are present, or vice versa.
- Regenerate the lockfile with a single resolution strategy per package.
- Split into two package entries if both forms are genuinely needed (note: pylock forbids this; reconsider).
Example fix
# before
[packages]
name = \"foo\"
wheels = [{ name = \"foo-1.0.whl\", hashes = {...} }]
vcs = { url = \"https://github.com/x/foo\", revision = \"v1.0\" }
# after
[packages]
name = \"foo\"
wheels = [{ name = \"foo-1.0.whl\", hashes = {...} }] Defensive patterns
Strategy: validation
Validate before calling
def is_exclusive_resolution(pkg: dict) -> bool:
has_dist = bool(pkg.get('sdist')) or bool(pkg.get('wheels'))
has_src = bool(pkg.get('vcs')) or bool(pkg.get('directory')) or bool(pkg.get('archive'))
return not (has_dist and has_src) Type guard
def has_no_dist_source_mix(pkg: dict) -> bool:
dists = bool(pkg.get('sdist')) + len(pkg.get('wheels') or [])
srcs = bool(pkg.get('vcs')) + bool(pkg.get('directory')) + bool(pkg.get('archive'))
return not (dists > 0 and srcs > 0) Try / catch
try:
PylockFile.from_dict(data)
except PylockValidationError as e:
if 'None of vcs, directory, archive' in str(e):
drop_source_locator_from_pkg(e.context) Prevention
- Pick one resolution strategy per package: artifacts OR source.
- Do not merge lockfile fragments from different strategies.
- Validate exclusivity before loading.
- Regenerate with a single resolver pass.
When it happens
Trigger: A [[packages]] table with both sdist = {...} (or wheels = [...]) and vcs = {...}; an entry with directory and wheels; an entry combining archive with sdist. The check counts distributions = bool(sdist)+len(wheels) and direct_urls = bool(vcs)+bool(directory)+bool(archive) and fires when both > 0.
Common situations: Merging two lockfile fragments (one pinned to artifacts, one to a Git URL) for the same package; hand-editing a lockfile to add a vcs override to an already-pinned package; tooling that emits both forms.
Related errors
- Exactly one of vcs, directory, archive must be set if sdist
- path or url must be provided
- Cannot determine sdist filename
- Cannot determine wheel filename
- Invalid wheel filename {wheel.filename!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/2caffe286d0817d1.json.
Report an issue: GitHub.