pypa/pip · error · UnsupportedWheel

.dist-info directory not found

Error message

.dist-info directory not found

What it means

Raised by wheel_dist_info_dir() when the wheel zip contains no top-level subdirectory ending in '.dist-info'. The .dist-info directory holds the wheel's metadata (METADATA, WHEEL, RECORD); its absence means the archive is not a valid wheel per the binary distribution format spec.

Solutions

  1. Inspect the wheel contents: 'unzip -l <file>.whl' and look for a .dist-info directory
  2. Rebuild the wheel with 'python -m build --wheel' or 'pip wheel'
  3. Re-download from the official index to rule out truncation
  4. If the file is actually a source archive, install it as a tarball/zip, not as a wheel

Example fix

// before
pip install ./myproj-1.0-py3-none-any.whl  # zip renamed to .whl
// after
# build a proper wheel with metadata
python -m build --wheel
pip install dist/myproj-1.0-py3-none-any.whl
Defensive patterns

Strategy: validation

Validate before calling

import zipfile

def wheel_has_dist_info(path: str) -> bool:
    try:
        with zipfile.ZipFile(path) as zf:
            top_dirs = {n.split('/')[0] for n in zf.namelist() if '/' in n}
            return any(d.endswith('.dist-info') for d in top_dirs)
    except (zipfile.BadZipFile, FileNotFoundError):
        return False

# before installing
if not wheel_has_dist_info(wheel_path):
    raise ValueError(f'{wheel_path} is missing a .dist-info directory')

Type guard

import zipfile

def has_single_dist_info(zf: zipfile.ZipFile) -> bool:
    dirs = {n.split('/')[0] for n in zf.namelist() if '/' in n}
    return sum(1 for d in dirs if d.endswith('.dist-info')) == 1

Try / catch

from pip._internal.exceptions import UnsupportedWheel

try:
    wheel_dist_info_dir(wheel_zip, name)
except UnsupportedWheel as e:
    if '.dist-info directory not found' in str(e):
        raise PackagingError(f'wheel is not a valid distribution: {e}')
    raise

Prevention

When it happens

Trigger: Calling wheel_dist_info_dir(source, name) on a ZipFile whose namelist() top-level entries include no directory matching '*.dist-info'. This is reached from parse_wheel() during any wheel installation.

Common situations: A zip file renamed to .whl that is not actually a wheel. A wheel built by a broken or incomplete build process that omitted metadata generation. A truncated download missing the metadata entries. A source archive mislabeled as a wheel.

Related errors


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

Appendix: source

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

    check_compatibility(version, name)

    return info_dir, metadata


def wheel_dist_info_dir(source: ZipFile, name: str) -> str:
    """Returns the name of the contained .dist-info directory.

    Raises AssertionError or UnsupportedWheel if not found, >1 found, or
    it doesn't match the provided name.
    """
    # 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

View on GitHub (pinned to f399c37189)