pypa/pip · error · UnsupportedWheel
could not read {path!r} file: {e!r}
Error message
could not read {path!r} file: {e!r} What it means
UnsupportedWheel raised by read_wheel_metadata_file when reading a file out of the wheel zip fails. It catches BadZipFile (general corruption), KeyError (entry missing — typically WHEEL or METADATA), and RuntimeError (password-protected zip). The path and the underlying exception repr are surfaced.
Source
Thrown at src/pip/_internal/utils/wheel.py:73
info_dir = info_dirs[0]
info_dir_name = canonicalize_name(info_dir)
canonical_name = canonicalize_name(name)
if not info_dir_name.startswith(canonical_name):
raise UnsupportedWheel(
f".dist-info directory {info_dir!r} does not start with {canonical_name!r}"
)
return info_dir
def read_wheel_metadata_file(source: ZipFile, path: str) -> bytes:
try:
return source.read(path)
# BadZipFile for general corruption, KeyError for missing entry,
# and RuntimeError for password-protected files
except (BadZipFile, KeyError, RuntimeError) as e:
raise UnsupportedWheel(f"could not read {path!r} file: {e!r}")
def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message:
"""Return the WHEEL metadata of an extracted wheel, if possible.
Otherwise, raise UnsupportedWheel.
"""
path = f"{dist_info_dir}/WHEEL"
# Zip file path separators must be /
wheel_contents = read_wheel_metadata_file(source, path)
try:
wheel_text = wheel_contents.decode()
except UnicodeDecodeError as e:
raise UnsupportedWheel(f"error decoding {path!r}: {e!r}")
# FeedParser (used by Parser) does not raise any exceptions. The returned
# message may have .defects populated, but for backwards-compatibility we
# currently ignore them.View on GitHub (pinned to d7d0d0a394)
Solutions
- Re-download and verify the hash against the index record.
- Inspect integrity: `unzip -t pkg.whl`.
- Confirm the .dist-info/WHEEL entry exists: `unzip -l pkg.whl | grep WHEEL`.
- Rebuild from clean dist/ if you produced the wheel yourself.
Example fix
// before pip install ./dist/pkg-1.0.whl # truncated zip -> BadZipFile // after rm -rf dist && python -m build --wheel && unzip -t dist/pkg-1.0-*.whl && pip install dist/pkg-1.0-*.whl
Defensive patterns
Strategy: validation
Validate before calling
import zipfile
def wheel_zip_intact(path: str) -> bool:
try:
with zipfile.ZipFile(path) as z:
return z.testzip() is None and any(n.endswith('/WHEEL') for n in z.namelist())
except (zipfile.BadZipFile, RuntimeError):
return False Type guard
null
Try / catch
null
Prevention
- Verify hashes after download.
- Inspect with `unzip -t` and `unzip -l` in CI.
- Don't re-encode/encrypt wheels.
- Use trusted transports to avoid truncation.
When it happens
Trigger: wheel_metadata calls source.read('<dist-info>/WHEEL') and the zip is corrupt (BadZipFile), the WHEEL entry is absent (KeyError), or the zip was encrypted (RuntimeError).
Common situations: Truncated download producing a structurally invalid zip; wheel that omits the WHEEL file entirely; encrypted/protected zip mislabeled as a wheel; disk corruption.
Related errors
- Bad metadata in {dist} (invalid metadata entry 'name')
- {name} has an invalid wheel, {e}
- .dist-info directory not found
- multiple .dist-info directories found: {}
- .dist-info directory {info_dir!r} does not start with {canon
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/02ca0dd7105eabd7.json.
Report an issue: GitHub.