pypa/pip · error · PylockValidationError

Cannot determine wheel filename

Error message

Cannot determine wheel filename

What it means

Raised as PylockValidationError by PackageWheel.filename (a computed property) in packaging.pylock when none of name, path, or url is set on a wheel entry, so the wheel filename cannot be derived. The filename is required by parse_wheel_filename for the wheel-name/version consistency checks in Package._from_dict.

Source

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

    @classmethod
    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
        package_wheel = cls(
            name=_get(d, str, "name"),
            upload_time=_get(d, datetime, "upload-time"),
            url=_get(d, str, "url"),
            path=_get(d, str, "path"),
            size=_get(d, int, "size"),
            hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"),  # type: ignore[type-abstract]
        )
        _validate_path_url(package_wheel.path, package_wheel.url)
        return package_wheel

    @property
    def filename(self) -> str:
        """Get the filename of the wheel."""
        filename = self.name or _path_name(self.path) or _url_name(self.url)
        if not filename:
            raise PylockValidationError("Cannot determine wheel filename")
        return filename


@dataclass(frozen=True, init=False)
class Package:
    name: NormalizedName
    version: Version | None = None
    marker: Marker | None = None
    requires_python: SpecifierSet | None = None
    dependencies: Sequence[Mapping[str, Any]] | None = None
    vcs: PackageVcs | None = None
    directory: PackageDirectory | None = None
    archive: PackageArchive | None = None
    index: str | None = None
    sdist: PackageSdist | None = None
    wheels: Sequence[PackageWheel] | None = None
    attestation_identities: Sequence[Mapping[str, Any]] | None = None
    tool: Mapping[str, Any] | None = None

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Provide name (e.g. 'pkg-1.0-py3-none-any.whl'), path, or url on each wheel entry.
  2. Regenerate the lockfile so every wheel records its filename or URL.
  3. Validate wheels before adding them to the package.
  4. Catch the error and report which wheel index is malformed (context).

Example fix

# before
[[packages.wheels]]
hashes = { sha256 = \"...\" }
# after
[[packages.wheels]]
name = \"pkg-1.0-py3-none-any.whl\"
hashes = { sha256 = \"...\" }
Defensive patterns

Strategy: validation

Validate before calling

def wheel_has_filename(w: dict) -> bool:
    return bool(w.get('name') or w.get('path') or w.get('url'))

Type guard

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

Try / catch

try:
    _ = wheel.filename
except PylockValidationError as e:
    if 'Cannot determine wheel filename' in str(e):
        w['name'] = f'{pkg.name}-{pkg.version}-py3-none-any.whl'

Prevention

When it happens

Trigger: A [[packages.wheels]] table with neither name, path, nor url; or a PackageWheel(...) constructed in code without any locator. Accessing .filename (often indirectly via the wheel validation loop at line ~597) triggers it.

Common situations: Lockfile with a wheel entry missing its locator fields; resolver that wrote the wheel hash but not the filename/URL; hand-editing that deleted the URL.

Related errors


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