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

  1. Open the WHEEL entry manually: `unzip -p pkg.whl '*/WHEEL' | head` and check it is readable text.
  2. Rebuild the wheel ensuring WHEEL is plain UTF-8 text generated by the backend.
  3. Re-download and verify the hash.
  4. 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

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


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/430e370bb5ac46dd.json. Report an issue: GitHub.