pypa/pip · error · InvalidWheelFilename

Invalid wheel filename (compressed tag set components must…

Error message

Invalid wheel filename (compressed tag set components must be in sorted order per PEP 425): {filename!r}

What it means

Raised by parse_wheel_filename() only when validate_order=True and the underlying parse_tag() detects that a compressed tag-set component is not in ascending sorted order (utils.py:294-298). Per PEP 425, members of a compressed tag set (e.g. the '.py2.py3' in 'py2.py3-none-any') must appear in sorted order. parse_tag raises UnsortedTagsError, which is re-raised as InvalidWheelFilename.

Solutions

  1. Sort each compressed tag-set component alphabetically (e.g. 'py3.py2' -> 'py2.py3').
  2. If order validation is not required for your use case, omit validate_order=True (defaults to False).
  3. Regenerate the wheel with a conforming build backend.

Example fix

// before
parse_wheel_filename('foo-1.0-py3.py2-none-any.whl', validate_order=True)
// after
parse_wheel_filename('foo-1.0-py2.py3-none-any.whl', validate_order=True)
Defensive patterns

Strategy: validation

Validate before calling

def safe_parse_wheel(filename: str, validate_order: bool = False):
    if validate_order:
        stem = filename[:-4]
        tag_str = stem.rsplit("-", 3)[-1]
        for component in tag_str.split("-"):
            members = component.split(".")
            if members != sorted(members):
                raise ValueError(f"Tag component not sorted: {component!r}")
    return parse_wheel_filename(filename, validate_order=validate_order)

Type guard

def tags_are_sorted(tag_str: str) -> bool:
    return all(
        parts == sorted(parts)
        for parts in (c.split(".") for c in tag_str.split("-"))
    )

Try / catch

from packaging.utils import InvalidWheelFilename

try:
    parse_wheel_filename(filename, validate_order=True)
except InvalidWheelFilename as e:
    if "sorted order" in str(e):
        logger.warning("Tag set not sorted in %s, retrying without validation", filename)
        parse_wheel_filename(filename, validate_order=False)

Prevention

When it happens

Trigger: Calling parse_wheel_filename('foo-1.0-py3.py2-none-any.whl', validate_order=True) where 'py3.py2' is not sorted (should be 'py2.py3').

Common situations: A wheel built by a tool that does not sort compressed tag-set members, or a manually constructed tag string where components were added out of order. Only triggered when the caller explicitly opts into order validation.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/46546ce6e755dffe. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_vendor/packaging/utils.py:295

        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(
            f"Invalid wheel filename (compressed tag set components must be in "
            f"sorted order per PEP 425): {filename!r}"
        ) from None
    except InvalidTag:
        raise InvalidWheelFilename(
            f"Invalid wheel filename (invalid tag component): {filename!r}"
        ) from None
    return (name, version, build, tags)


def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
    """
    This function takes the filename of a sdist file (as specified
    in the `Source distribution format`_ documentation), and parses
    it, returning a tuple of the normalized name and version as
    represented by an instance of :class:`~packaging.version.Version`.

    :param str filename: The name of the sdist file.

View on GitHub (pinned to f399c37189)