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
- Inspect the wheel contents: 'unzip -l <file>.whl' and look for a .dist-info directory
- Rebuild the wheel with 'python -m build --wheel' or 'pip wheel'
- Re-download from the official index to rule out truncation
- 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
- Build wheels with compliant backends that always generate .dist-info
- Verify wheel structure with 'unzip -l' before installing
- Do not rename non-wheel zips to .whl
- Re-download from the official index
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
- multiple .dist-info directories found
- .dist-info directory
- invalid Wheel-Version
- has an invalid wheel
- WHEEL is missing Wheel-Version
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)