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
- Sort each compressed tag-set component alphabetically (e.g. 'py3.py2' -> 'py2.py3').
- If order validation is not required for your use case, omit validate_order=True (defaults to False).
- 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
- Sort each compressed tag-set component alphabetically before embedding in a filename
- Only pass validate_order=True when strict PEP 425 compliance is required
- Use a conforming build backend that emits sorted tag sets
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
- Invalid wheel filename (invalid tag component)
- Invalid build number
- Invalid project name
- Invalid wheel filename (extension must be '.whl')
- Invalid wheel filename (invalid version)
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)