pypa/pip · error · UnsupportedWheel

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

Error message

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

What it means

UnsupportedWheel raised when the wheel's .dist-info directory name (canonicalized) does not start with the canonicalized wheel/project name. This catches wheels whose metadata directory is misnamed relative to the wheel filename — a sign of tampering or a broken build. The offending info_dir and expected canonical name are both included.

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 d7d0d0a394)

Solutions

  1. Inspect the .dist-info dir name with `unzip -l pkg.whl`.
  2. Don't rename wheels after building; rebuild with the correct name.
  3. Use a PEP 517 backend so name canonicalization is consistent between filename and .dist-info.
  4. Confirm the requirement name spelling matches the wheel filename.

Example fix

// before
# wheel built as Foo but renamed to Bar-1.0-py3-none-any.whl
mv Foo-1.0-py3-none-any.whl Bar-1.0-py3-none-any.whl
pip install Bar-1.0-py3-none-any.whl

// after
# rebuild with the correct distribution name:
python -m build --wheel
Defensive patterns

Strategy: validation

Validate before calling

import zipfile, re
from pip._vendor.packaging.utils import canonicalize_name  # or packaging.utils
def name_matches(path: str) -> bool:
    wheel_name = os.path.basename(path)[:-4]  # strip .whl
    project = wheel_name.split('-')[0]
    canonical = canonicalize_name(project)
    with zipfile.ZipFile(path) as z:
        info_dirs = [n.split('/',1)[0] for n in z.namelist() if n.split('/',1)[0].endswith('.dist-info')]
    return len(info_dirs) == 1 and canonicalize_name(info_dirs[0]).startswith(canonical)

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: wheel_dist_info_dir finds exactly one .dist-info but its canonicalized name doesn't begin with canonicalize_name(name). For a wheel `Foo-1.0-py3-none-any.whl`, the dir must canonicalize to start with `foo`.

Common situations: Wheel renamed after build so filename and metadata dir disagree; backend that normalizes names inconsistently; manually zipped wheel with a typo'd .dist-info dir name; case mismatch (e.g. `Foo` vs `foo`).

Related errors


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