pypa/pip · error · InvalidWheelFilename
Invalid wheel filename (wrong number of parts): {filename!r}
Error message
Invalid wheel filename (wrong number of parts): {filename!r} What it means
Raised by `parse_wheel_filename` when the stem (filename minus `.whl`) does not contain exactly 4 or 5 dashes. A well-formed wheel filename has 5 dash-separated parts (name, version, pytag, abitag, platformtag) or 6 when a build tag is present, so after stripping `.whl` the dash count must be 4 (no build) or 5 (with build). Any other count means the filename is structurally broken.
Source
Thrown at src/pip/_vendor/packaging/utils.py:210
>>> ver == Version('1.0')
True
>>> tags == {Tag("py3", "none", "any")}
True
>>> not build
True
.. versionadded:: 26.1
The *validate_order* parameter.
"""
if not filename.endswith(".whl"):
raise InvalidWheelFilename(
f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
)
filename = filename[:-4]
dashes = filename.count("-")
if dashes not in (4, 5):
raise InvalidWheelFilename(
f"Invalid wheel filename (wrong number of parts): {filename!r}"
)
parts = filename.split("-", dashes - 2)
name_part = parts[0]
# See PEP 427 for the rules on escaping the project name.
if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
raise InvalidWheelFilename(f"Invalid project name: {filename!r}")
name = canonicalize_name(name_part)
try:
version = Version(parts[1])
except InvalidVersion as e:
raise InvalidWheelFilename(
f"Invalid wheel filename (invalid version): {filename!r}"
) from e
if dashes == 5:View on GitHub (pinned to d7d0d0a394)
Solutions
- Rebuild the wheel with a compliant backend (setuptools/hatchling/flit) which escapes `-` in the name to `_`.
- Rename so name/version contain no unescaped dashes: replace `-` with `_` in the name and version segments, e.g. `my_pkg-1.0-py3-none-any.whl`.
- Confirm the expected 5-part (or 6-part with build) structure before parsing.
- Discard/re-download the artifact if externally produced and malformed.
Example fix
// before
parse_wheel_filename('my-pkg-1.0-py3-none-any.whl') # 5 dashes -> raises
# after
parse_wheel_filename('my_pkg-1.0-py3-none-any.whl') # 4 dashes -> ok Defensive patterns
Strategy: try-catch
Validate before calling
def has_wheel_dash_count(filename: str) -> bool:
if not filename.endswith('.whl'):
return False
return filename[:-4].count('-') in (4, 5) Type guard
def is_well_formed_wheel(filename: object) -> bool:
if not isinstance(filename, str) or not filename.endswith('.whl'):
return False
return filename[:-4].count('-') in (4, 5) Try / catch
from pip._vendor.packaging.utils import parse_wheel_filename, InvalidWheelFilename
try:
name, ver, build, tags = parse_wheel_filename(filename)
except InvalidWheelFilename as e:
if 'wrong number of parts' in str(e):
# rebuild or rename so the name has no unescaped dashes
...
raise Prevention
- Use a compliant build backend so project-name dashes are escaped to underscores in wheel filenames.
- Never hand-edit wheel filenames.
- Pre-validate the dash count is 4 or 5 after stripping `.whl`.
When it happens
Trigger: A wheel filename with a project name or version containing literal dashes that were not escaped to underscores during build, or a missing/extra tag component. Example: `my-pkg-1.0-py3-none-any.whl` (the unescaped dash in `my-pkg` inflates the dash count).
Common situations: Hand-renamed wheel files; project names built with hyphens that the build backend failed to normalize; truncation/corruption of the filename; mixing in non-wheel files that happen to end in `.whl`.
Related errors
- Invalid wheel filename (extension must be '.whl'): {filename
- Invalid project name: {filename!r}
- Invalid build number: {build_part} in {filename!r}
- Invalid wheel filename (invalid version): {filename!r}
- Invalid wheel filename (compressed tag set components must b
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/644c4d38196042d5.json.
Report an issue: GitHub.