pypa/pip · error · PylockValidationError
Exactly one of vcs, directory, archive must be set if sdist…
Error message
Exactly one of vcs, directory, archive must be set if sdist and wheels are not set
What it means
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.
Solutions
- Add exactly one of vcs/directory/archive to the package entry.
- If two are present, remove one so only a single source remains.
Example fix
# before [[packages]] name = "x" version = "1.0" # after [[packages]] name = "x" version = "1.0" [packages.vcs] type = "git" url = "https://example.com/x.git" commit-id = "abc123"
Defensive patterns
Strategy: validation
Validate before calling
def package_has_exactly_one_source(pkg) -> bool:
distributions = bool(pkg.get("sdist")) + len(pkg.get("wheels") or [])
direct = bool(pkg.get("vcs")) + bool(pkg.get("directory")) + bool(pkg.get("archive"))
if distributions > 0:
return True
return direct == 1
Try / catch
from packaging.pylock import Pylock, PylockValidationError
try:
Pylock.from_dict(d)
except PylockValidationError as e:
...
Prevention
- Every direct-reference package must declare exactly one of vcs/directory/archive.
- Remember 'index' is not a substitute for a direct source.
When it happens
Trigger: 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).
Common situations: 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).
Related errors
- None of vcs, directory, archive must be set if sdist or…
- At least one hash must be provided
- Cannot determine sdist filename
- Cannot determine wheel filename
- Cannot select requirements from pylock file
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/0fef78e18fd342ec.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/pylock.py:609
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 "
f"package name {package.name!r}",
context=f"wheels[{i}]",
)
if package.version and version != package.version:View on GitHub (pinned to f399c37189)