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
- Inspect the .dist-info dir name with `unzip -l pkg.whl`.
- Don't rename wheels after building; rebuild with the correct name.
- Use a PEP 517 backend so name canonicalization is consistent between filename and .dist-info.
- 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
- Never rename wheel files after building.
- Use a standard backend so name canonicalization is consistent.
- Verify filename spelling against metadata.
- Run `twine check`.
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
- {name} has an invalid wheel, {e}
- multiple .dist-info directories found: {}
- error decoding {path!r}: {e!r}
- WHEEL is missing Wheel-Version
- invalid Wheel-Version: {version!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/577d57f8b3a44f25.json.
Report an issue: GitHub.