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

  1. Verify the version segment is a valid PEP 440 version string.
  2. Test the version with Version(version_str) in isolation to get the precise parse error.
  3. 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

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


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)