pypa/pip · error · UnsupportedWheel
error decoding
Error message
error decoding {path!r}: {e!r} What it means
Raised by wheel_metadata() when the WHEEL metadata file content cannot be decoded as text (UnicodeDecodeError). The WHEEL file must be ASCII or UTF-8 text; a decode failure means the file contains invalid byte sequences.
Solutions
- Inspect the WHEEL file encoding: 'unzip -p <file>.whl "*.dist-info/WHEEL" | file -'
- Rebuild the wheel with a standard build backend (setuptools/hatchling/flit)
- If you control the build, ensure the WHEEL file is written as UTF-8 or ASCII
- Re-download the wheel from the official index
Example fix
// before pip install ./handmade-1.0-py3-none-any.whl # WHEEL file in Latin-1 // after # rebuild with a standard backend that emits UTF-8 metadata python -m build --wheel pip install dist/handmade-1.0-py3-none-any.whl
Defensive patterns
Strategy: validation
Validate before calling
import zipfile
def wheel_metadata_is_decodable(path: str) -> bool:
with zipfile.ZipFile(path) as zf:
for name in zf.namelist():
if name.endswith('.dist-info/WHEEL'):
try:
zf.read(name).decode()
return True
except UnicodeDecodeError:
return False
return False Type guard
def is_decodable_text(data: bytes) -> bool:
try:
data.decode('utf-8')
return True
except UnicodeDecodeError:
return False Try / catch
from pip._internal.exceptions import UnsupportedWheel
try:
wheel_metadata(wheel_zip, dist_info_dir)
except UnsupportedWheel as e:
if 'error decoding' in str(e):
raise PackagingError(f'WHEEL file has invalid encoding: {e}')
raise Prevention
- Build wheels with standard backends that write UTF-8 metadata
- Never hand-edit WHEEL files in a non-UTF-8 encoding
- Verify encoding: 'unzip -p <file>.whl *.dist-info/WHEEL | file -'
- Rebuild from source if encoding is wrong
When it happens
Trigger: Calling wheel_metadata(source, dist_info_dir) where the WHEEL file bytes raise UnicodeDecodeError on .decode(). Reached during wheel installation via parse_wheel().
Common situations: A wheel built by a tool that wrote the WHEEL file in a non-UTF-8 encoding (e.g. Latin-1 with non-ASCII characters). A corrupt WHEEL file containing binary garbage. A wheel assembled by hand with incorrectly encoded metadata.
Related errors
- could not read file
- .dist-info directory
- .dist-info directory not found
- Error decoding metadata for
- Error decoding metadata for
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/430e370bb5ac46dd.
Report an issue: GitHub.
Appendix: 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 f399c37189)