pypa/pip · error · UnsupportedWheel
error decoding {path!r}: {e!r}
Error message
error decoding {path!r}: {e!r} What it means
UnsupportedWheel raised when the WHEEL metadata file was read successfully (raw bytes) but cannot be decoded as text. pip calls wheel_contents.decode() expecting UTF-8; a UnicodeDecodeError is wrapped with the path and the error repr. It indicates the WHEEL file is binary garbage or in an unexpected encoding.
Source
Thrown at src/pip/_internal/utils/wheel.py:87
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.
return Parser().parsestr(wheel_text)
def wheel_version(wheel_data: Message) -> tuple[int, ...]:
"""Given WHEEL metadata, return the parsed Wheel-Version.
Otherwise, raise UnsupportedWheel.
"""
version_text = wheel_data["Wheel-Version"]
if version_text is None:
raise UnsupportedWheel("WHEEL is missing Wheel-Version")
version = version_text.strip()
try:View on GitHub (pinned to d7d0d0a394)
Solutions
- Open the WHEEL entry manually: `unzip -p pkg.whl '*/WHEEL' | head` and check it is readable text.
- Rebuild the wheel ensuring WHEEL is plain UTF-8 text generated by the backend.
- Re-download and verify the hash.
- Inspect with `twine check` which validates wheel metadata before publishing.
Example fix
// before # custom build wrote a binary blob as WHEEL pip install pkg-1.0-py3-none-any.whl // after # let the backend write WHEEL correctly; rebuild: rm -rf dist && python -m build --wheel && twine check dist/*
Defensive patterns
Strategy: validation
Validate before calling
import zipfile
def wheel_file_decodes(path: str) -> bool:
with zipfile.ZipFile(path) as z:
wheel_entry = next((n for n in z.namelist() if n.endswith('/WHEEL')), None)
if not wheel_entry:
return False
try:
z.read(wheel_entry).decode('utf-8')
return True
except UnicodeDecodeError:
return False Type guard
null
Try / catch
null
Prevention
- Use a PEP 517 backend to generate WHEEL as UTF-8 text.
- Run `twine check` to validate metadata encoding.
- Never write binary into WHEEL during custom builds.
- Verify download integrity.
When it happens
Trigger: wheel_metadata reads <dist-info>/WHEEL bytes, then .decode() raises UnicodeDecodeError because the bytes aren't valid UTF-8 (e.g. a binary placeholder, encryption artifact, or wrong file written into the WHEEL slot).
Common situations: Wheel build script that wrote binary data into the WHEEL path; corrupt download leaving non-UTF-8 bytes; a tool that accidentally included the wrong file as WHEEL.
Related errors
- {name} has an invalid wheel, {e}
- multiple .dist-info directories found: {}
- .dist-info directory {info_dir!r} does not start with {canon
- WHEEL is missing Wheel-Version
- invalid Wheel-Version: {version!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/430e370bb5ac46dd.json.
Report an issue: GitHub.