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

  1. Inspect the WHEEL file encoding: 'unzip -p <file>.whl "*.dist-info/WHEEL" | file -'
  2. Rebuild the wheel with a standard build backend (setuptools/hatchling/flit)
  3. If you control the build, ensure the WHEEL file is written as UTF-8 or ASCII
  4. 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

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


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)