pypa/pip · error · InvalidWheelFilename
Invalid wheel filename (invalid version)
Error message
Invalid wheel filename (invalid version): {filename!r} What it means
Raised by parse_wheel_filename() when the version segment of the wheel stem cannot be parsed by Version() (utils.py:274-279). The inner InvalidVersion from version.py is caught and re-raised as InvalidWheelFilename. The version must conform to PEP 440.
Solutions
- Verify the version segment is a valid PEP 440 version string.
- Test the version with Version(version_str) in isolation to get the precise parse error.
- Normalize the version string to PEP 440 before building the wheel.
Example fix
// before
parse_wheel_filename('foo-1.0.alpha-py3-none-any.whl') // '1.0.alpha' is not PEP 440
// after
parse_wheel_filename('foo-1.0a1-py3-none-any.whl') Defensive patterns
Strategy: try-catch
Validate before calling
from packaging.version import Version, InvalidVersion
def safe_parse_wheel(filename: str):
stem = filename[:-4]
version_str = stem.split("-", 2)[1]
try:
Version(version_str)
except InvalidVersion:
raise ValueError(f"Invalid version {version_str!r} in wheel {filename!r}")
return parse_wheel_filename(filename) Type guard
from packaging.version import Version
def has_valid_wheel_version(filename: str) -> bool:
if not isinstance(filename, str) or not filename.endswith(".whl"):
return False
try:
Version(filename[:-4].split("-", 2)[1])
return True
except Exception:
return False Try / catch
from packaging.utils import InvalidWheelFilename
try:
name, ver, build, tags = parse_wheel_filename(filename)
except InvalidWheelFilename as e:
if "invalid version" in str(e):
logger.error("Wheel version is not PEP 440 compliant: %s", filename)
raise Prevention
- Test the version segment with Version() in isolation first
- Ensure the build tool produces PEP 440-compliant versions
- Normalize version strings before constructing wheel filenames
When it happens
Trigger: A wheel filename whose second dash-separated component is not a valid PEP 440 version, e.g. 'foo-notaversion-py3-none-any.whl' where 'notaversion' fails Version() parsing.
Common situations: Non-PEP-440 version schemes (e.g. SemVer-only dates like '20230101-rc1' with unexpected format), or a build tool that emits a version string not understood by packaging's Version class.
Related errors
- Invalid sdist filename (invalid version)
- dev must be non-negative integer, got
- epoch must be non-negative integer, got
- Invalid build number
- Invalid project name
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/296d5f8112de648e.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/utils.py:277
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 _wheel_name_regex.match(name_part) 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:
build_part = parts[2]
build_match = _build_tag_regex.match(build_part)
if build_match is None:
raise InvalidWheelFilename(
f"Invalid build number: {build_part} in {filename!r}"
)
build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2)))
else:
build = ()
tag_str = parts[-1]
try:
tags = parse_tag(tag_str, validate_order=validate_order)
except UnsortedTagsError:
raise InvalidWheelFilename(View on GitHub (pinned to f399c37189)