pypa/pip · error · InvalidWheel

Wheel '{name}' located at {location} is invalid.

Error message

Wheel '{name}' located at {location} is invalid.

What it means

InvalidWheel raised by Distribution.from_wheel() (importlib backend) when wheel.as_zipfile() fails with zipfile.BadZipFile - the file is named *.whl but is not a valid ZIP archive, so it cannot be opened/read at all. Distinct from UnsupportedWheel (bad contents) in that the file itself is not even a zip.

Source

Thrown at src/pip/_internal/metadata/importlib/_dists.py:142

        project_name: str,
    ) -> BaseDistribution:
        # Generate temp dir to contain the metadata file, and write the file contents.
        temp_dir = pathlib.Path(
            TempDirectory(kind="metadata", globally_managed=True).path
        )
        metadata_path = temp_dir / "METADATA"
        metadata_path.write_bytes(metadata_contents)
        # Construct dist pointing to the newly created directory.
        dist = importlib.metadata.Distribution.at(metadata_path.parent)
        return cls(dist, metadata_path.parent, None)

    @classmethod
    def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
        try:
            with wheel.as_zipfile() as zf:
                dist = WheelDistribution.from_zipfile(zf, name, wheel.location)
        except zipfile.BadZipFile as e:
            raise InvalidWheel(wheel.location, name) from e
        return cls(dist, dist.info_location, pathlib.PurePosixPath(wheel.location))

    @property
    def location(self) -> str | None:
        if self._info_location is None:
            return None
        return str(self._info_location.parent)

    @property
    def info_location(self) -> str | None:
        if self._info_location is None:
            return None
        return str(self._info_location)

    @property
    def installed_location(self) -> str | None:
        if self._installed_location is None:
            return None

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Clear the pip cache (pip cache purge) and re-download the wheel.
  2. Verify the wheel hash if one is pinned; a mismatch confirms corruption.
  3. Re-download from PyPI directly to rule out a proxy/mirror serving bad files.
  4. If building locally, rebuild and re-export the wheel cleanly.

Example fix

# before - corrupted cached wheel
pip install somepkg

# after
pip cache purge
pip install --no-cache-dir somepkg
Defensive patterns

Strategy: validation

Validate before calling

import zipfile
try:
    with zipfile.ZipFile(path) as zf:
        bad = zf.testzip()  # None if OK
    print('valid zip' if bad is None else f'corrupt entry: {bad}')
except zipfile.BadZipFile:
    print('not a valid zip / wheel')

Type guard

def is_valid_wheel_zip(path: str) -> bool:
    import zipfile
    try:
        with zipfile.ZipFile(path) as zf:
            return zf.testzip() is None
    except zipfile.BadZipFile:
        return False

Try / catch

from pip._internal.exceptions import InvalidWheel
try:
    dist = Distribution.from_wheel(wheel, name)
except InvalidWheel:
    # purge cache and re-download
    ...

Prevention

When it happens

Trigger: Reached while preparing a wheel-based distribution: opening the .whl with zipfile raises BadZipFile because the file is truncated, corrupted, or not actually a zip (e.g. an HTML error page saved as .whl, a partial download).

Common situations: A truncated download (network drop) leaving an incomplete .whl; a cache (pip cache) containing a corrupted file; a custom index returning an error page with a .whl URL; disk corruption.

Related errors


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