pypa/pip · error · InvalidWheel
Wheel ' ' located at is invalid.
Error message
Wheel '{name}' located at {location} is invalid. What it means
Raised as InvalidWheel from Distribution.from_wheel in pkg_resources.py:139-149 when wheel.as_zipfile() raises zipfile.BadZipFile. The pkg_resources-based metadata backend opens the wheel as a zip to extract the dist-info metadata; a structurally corrupt wheel triggers BadZipFile, wrapped as InvalidWheel(location, name).
Solutions
- Purge pip's cache (pip cache purge) and re-download the wheel.
- Verify the wheel size/hash against the index; re-download if mismatched.
- Rebuild the wheel locally if it is your own package.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before opening the wheel, verify it is a complete, valid zip:
import zipfile
if not zipfile.is_zipfile(wheel.location):
raise SystemExit(f'{wheel.location} corrupt; re-download') Try / catch
from pip._internal.exceptions import InvalidWheel
try:
dist = Distribution.from_wheel(wheel, name)
except InvalidWheel:
# purge cache and re-download
... Prevention
- Verify wheel hashes after download to detect corruption.
- Clear pip cache when BadZipFile/InvalidWheel recurs.
- Use checksummed, retried downloads in CI.
When it happens
Trigger: Distribution.from_wheel(wheel, name) in the pkg_resources backend; the zip is corrupt/truncated so zipfile.BadZipFile is raised and re-raised as InvalidWheel.
Common situations: Corrupted/partial wheel download (network/proxy issue), a file not actually a zip renamed .whl, disk corruption, or a stale partial file in the cache.
Related errors
- could not read file
- Wheel ' ' located at is invalid.
- Error decoding metadata for
- has an invalid wheel
- `base` parameter in `_fn` is `None`. Either override this…
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/922f5c6255a691e9.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_internal/metadata/pkg_resources.py:149
dist = pkg_resources.DistInfoDistribution(
location=filename,
metadata=InMemoryMetadata(metadata_dict, filename),
project_name=project_name,
)
return cls(dist)
@classmethod
def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
try:
with wheel.as_zipfile() as zf:
info_dir, _ = parse_wheel(zf, name)
metadata_dict = {
path.split("/", 1)[-1]: read_wheel_metadata_file(zf, path)
for path in zf.namelist()
if path.startswith(f"{info_dir}/")
}
except zipfile.BadZipFile as e:
raise InvalidWheel(wheel.location, name) from e
except UnsupportedWheel as e:
raise UnsupportedWheel(f"{name} has an invalid wheel, {e}")
dist = pkg_resources.DistInfoDistribution(
location=wheel.location,
metadata=InMemoryMetadata(metadata_dict, wheel.location),
project_name=name,
)
return cls(dist)
@property
def location(self) -> str | None:
return self._dist.location
@property
def installed_location(self) -> str | None:
egg_link = egg_link_path_from_location(self.raw_name)
if egg_link:
location = egg_linkView on GitHub (pinned to f399c37189)