pypa/pip · error · UnsupportedWheel

.dist-info directory

Error message

.dist-info directory {info_dir!r} does not start with {canonical_name!r}

What it means

Raised by wheel_dist_info_dir() when the single .dist-info directory name, after canonicalization (PEP 503 normalization), does not start with the canonicalized wheel name. This check ensures the metadata directory corresponds to the distribution being installed, catching mismatches between the wheel filename and its internal metadata.

Solutions

  1. Compare the wheel filename's project name with the .dist-info directory name inside it
  2. Rebuild the wheel ensuring the project name in pyproject.toml/setup.py matches the filename
  3. Re-download the wheel from the official index to ensure filename and contents are consistent
  4. Do not manually rename wheel files; rebuild them with the correct name

Example fix

// before
# wheel renamed from old-name-1.0.whl to new-name-1.0.whl
pip install ./new-name-1.0-py3-none-any.whl
// after
# set the correct project name in pyproject.toml then rebuild
[project]
name = "new-name"
python -m build --wheel
pip install dist/new-name-1.0-py3-none-any.whl
Defensive patterns

Strategy: validation

Validate before calling

import zipfile
from pip._vendor.packaging.utils import canonicalize_name
import os

def wheel_name_matches_dist_info(path: str) -> bool:
    # extract project name from filename: name-version-pythontag-abitag-plattag.whl
    wheel_name = os.path.basename(path).split('-', 1)[0]
    canonical_wheel = canonicalize_name(wheel_name)
    with zipfile.ZipFile(path) as zf:
        dirs = [n.split('/')[0] for n in zf.namelist() if '/' in n]
        info_dirs = [d for d in set(dirs) if d.endswith('.dist-info')]
        if len(info_dirs) != 1:
            return False
        return canonicalize_name(info_dirs[0]).startswith(canonical_wheel)

Type guard

from pip._vendor.packaging.utils import canonicalize_name

def dist_info_matches_name(info_dir: str, wheel_name: str) -> bool:
    return canonicalize_name(info_dir).startswith(canonicalize_name(wheel_name))

Try / catch

from pip._internal.exceptions import UnsupportedWheel

try:
    wheel_dist_info_dir(wheel_zip, name)
except UnsupportedWheel as e:
    if 'does not start with' in str(e):
        raise PackagingError(f'wheel filename/metadata mismatch: {e}')
    raise

Prevention

When it happens

Trigger: Calling wheel_dist_info_dir(source, name) where canonicalize_name(info_dir) does not start with canonicalize_name(name). For example, a wheel named 'foo-bar-1.0.whl' whose .dist-info is 'baz-1.0.dist-info'.

Common situations: A wheel manually renamed to a different distribution name without rebuilding. A build backend configured with the wrong project name. A vendored or repackaged wheel whose filename was changed but contents were not. A typo-squatting package mimicking another name.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/577d57f8b3a44f25. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_internal/utils/wheel.py:60

    # Zip file path separators must be /
    subdirs = {p.split("/", 1)[0] for p in source.namelist()}

    info_dirs = [s for s in subdirs if s.endswith(".dist-info")]

    if not info_dirs:
        raise UnsupportedWheel(".dist-info directory not found")

    if len(info_dirs) > 1:
        raise UnsupportedWheel(
            "multiple .dist-info directories found: {}".format(", ".join(info_dirs))
        )

    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.

View on GitHub (pinned to f399c37189)