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

  1. Re-download and verify the hash against the index record.
  2. Inspect integrity: `unzip -t pkg.whl`.
  3. Confirm the .dist-info/WHEEL entry exists: `unzip -l pkg.whl | grep WHEEL`.
  4. 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

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


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