pypa/pip · error · PylockValidationError

path or url must be provided

Error message

path or url must be provided

What it means

Raised as PylockValidationError by _validate_path_url in packaging.pylock when both 'path' and 'url' are missing/empty for an archive, sdist, wheel, vcs, or directory entry. At least one locator is required so the artifact can be resolved.

Source

Thrown at src/pip/_vendor/packaging/pylock.py:238

    d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
) -> Sequence[_FromMappingProtocolT]:
    """Get a required list value from the dictionary and convert its items to a
    dataclass."""
    if (result := _get_sequence_of_objects(d, target_item_type, key)) is None:
        raise _PylockRequiredKeyError(key)
    return result


def _validate_normalized_name(name: str) -> NormalizedName:
    """Validate that a string is a NormalizedName."""
    if not is_normalized_name(name):
        raise PylockValidationError(f"Name {name!r} is not normalized")
    return NormalizedName(name)


def _validate_path_url(path: str | None, url: str | None) -> None:
    if not path and not url:
        raise PylockValidationError("path or url must be provided")


def _path_name(path: str | None) -> str | None:
    if not path:
        return None
    # If the path is relative it MAY use POSIX-style path separators explicitly
    # for portability
    if "/" in path:
        return path.rsplit("/", 1)[-1]
    elif "\\" in path:
        return path.rsplit("\\", 1)[-1]
    else:
        return path


def _url_name(url: str | None) -> str | None:
    if not url:
        return None

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Add a 'url' (preferred for published artifacts) or 'path' (for local) to the offending table.
  2. Regenerate the lockfile so the resolver fills in the locator.
  3. Check for case/spelling mistakes in the key name.
  4. Catch the error and report the package name to the user for fixing.

Example fix

# before
[packages.sdist]
hashes = { sha256 = \"...\" }
# after
[packages.sdist]
url = \"https://files.pypi.org/.../pkg-1.0.tar.gz\"
hashes = { sha256 = \"...\" }
Defensive patterns

Strategy: validation

Validate before calling

def has_locator(entry: dict) -> bool:
    return bool(entry.get('path')) or bool(entry.get('url'))

Type guard

def has_path_or_url(d) -> bool:
    return bool(d.get('path')) or bool(d.get('url'))

Try / catch

from packaging.pylock import PylockValidationError
try:
    PylockFile.from_dict(data)
except PylockValidationError as e:
    if 'path or url' in str(e):
        add_url_to_entry(e.context)

Prevention

When it happens

Trigger: A pylock package entry with an [[packages.sdist]] table that omits both path and url; an archive = {} inline table with neither key; a wheel entry where both fields were accidentally deleted.

Common situations: Hand-editing the lockfile and removing the locator; a buggy resolver that wrote an entry before computing the URL; TOML where the key was misspelled (e.g. 'Url' vs 'url').

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/c09d1847cb436890.json. Report an issue: GitHub.