pypa/pip · error · InvalidWheelFilename

Invalid wheel filename (invalid tag component): {filename!r}

Error message

Invalid wheel filename (invalid tag component): {filename!r}

What it means

Raised by parse_wheel_filename() when parse_tag() raises InvalidTag (utils.py:299-302). This covers three cases per tags.py:301-304: the tag string does not have exactly three dash-separated components, any component is empty (e.g. trailing dash or double dash), or an interpreter component is not a valid Python identifier.

Source

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

        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.
    :raises InvalidSdistFilename: If the filename does not end
        with an sdist extension (``.zip`` or ``.tar.gz``), if it does not
        contain a dash separating the name and the version of the distribution,
        if the project name is empty, or if the version portion is not a valid
        version.

View on GitHub (pinned to f399c37189)

Solutions

  1. Ensure the tag portion has exactly three components: {pythontag}-{abitag}-{platformtag}.
  2. Remove any empty components (no double dashes or trailing dashes).
  3. Verify the interpreter tag starts with a letter or underscore (must be a valid Python identifier).

Example fix

// before
parse_wheel_filename('foo-1.0-py3-none-any-extra.whl')  // 4 tag components
// after
parse_wheel_filename('foo-1.0-py3-none-any.whl')
Defensive patterns

Strategy: validation

Validate before calling

def safe_parse_wheel(filename: str):
    stem = filename[:-4]
    tag_str = stem.rsplit("-", 3)[-1]
    components = tag_str.split("-")
    if len(components) != 3:
        raise ValueError(f"Tag must have 3 components: {tag_str!r}")
    for comp in components:
        if "" in comp.split("."):
            raise ValueError(f"Empty tag component in: {tag_str!r}")
    for interp in components[0].split("."):
        if not interp.isidentifier():
            raise ValueError(f"Invalid interpreter tag: {interp!r}")
    return parse_wheel_filename(filename)

Type guard

def is_valid_tag_string(tag_str: str) -> bool:
    parts = tag_str.split("-")
    if len(parts) != 3:
        return False
    for component in parts:
        if "" in component.split("."):
            return False
    return all(interp.isidentifier() for interp in parts[0].split("."))

Try / catch

from packaging.utils import InvalidWheelFilename

try:
    name, ver, build, tags = parse_wheel_filename(filename)
except InvalidWheelFilename as e:
    if "invalid tag component" in str(e):
        logger.error("Malformed tag in wheel: %s", filename)
    raise

Prevention

When it happens

Trigger: A wheel filename whose tag portion is malformed: 'foo-1.0-py3-none-any-extra.whl' (4 components), 'foo-1.0-py3--any.whl' (empty ABI), or 'foo-1.0-3py-none-any.whl' (interpreter '3py' is not a valid identifier because it starts with a digit).

Common situations: Hand-crafted wheel filenames with wrong tag structure, a platform tag containing a stray dash, or a build tool that omits or duplicates a tag component.

Related errors


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