pypa/pip · error · InvalidWheelFilename

Invalid project name: {filename!r}

Error message

Invalid project name: {filename!r}

What it means

Raised by `parse_wheel_filename` when the name segment of the wheel filename is itself invalid: it contains a literal `__` (double underscore, which is reserved for escaping runs of `_-` in project names) or does not match `^[\w\d._]*$` (alphanumerics, underscore, dot, hyphen). This guards PEP 427's name-escaping rules before canonicalization.

Source

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

       The *validate_order* parameter.
    """
    if not filename.endswith(".whl"):
        raise InvalidWheelFilename(
            f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
        )

    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 re.match(r"^[\w\d._]*$", name_part, re.UNICODE) 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:

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Rebuild the wheel with a current, PEP 427-compliant backend that escapes project names correctly.
  2. Rename the name segment to use only `[A-Za-z0-9._-]` and no `__` runs.
  3. Validate the upstream distribution name (use `canonicalize_name(name, validate=True)`).
  4. If the artifact came from an external index, report/fallback and re-fetch a valid wheel.

Example fix

// before
parse_wheel_filename('foo__bar-1.0-py3-none-any.whl')  # '__' in name -> raises

# after
parse_wheel_filename('foo_bar-1.0-py3-none-any.whl')
Defensive patterns

Strategy: try-catch

Validate before calling

import re

def has_valid_wheel_name_segment(filename: str) -> bool:
    if not filename.endswith('.whl'):
        return False
    stem = filename[:-4]
    name_part = stem.split('-', stem.count('-') - 2)[0]
    return '__' not in name_part and bool(re.match(r'^[\w\d._]*$', name_part, re.UNICODE))

Type guard

import re

def is_valid_wheel_name_segment(filename: object) -> bool:
    if not isinstance(filename, str) or not filename.endswith('.whl'):
        return False
    stem = filename[:-4]
    name_part = stem.split('-', max(1, stem.count('-') - 2))[0]
    return '__' not in name_part and bool(re.match(r'^[\w\d._]*$', name_part, re.UNICODE))

Try / catch

from pip._vendor.packaging.utils import parse_wheel_filename, InvalidWheelFilename

try:
    parse_wheel_filename(filename)
except InvalidWheelFilename as e:
    if 'Invalid project name' in str(e):
        # rename the name segment to alphanumerics + _ . - with no __
        ...
    raise

Prevention

When it happens

Trigger: A wheel whose name segment contains `__` (e.g. from a name with consecutive `-`/`_` not properly escaped), or characters outside `[A-Za-z0-9_.-]` such as spaces, parentheses, or plus signs. The check is `'__' in name_part or re.match(r"^[\w\d._]*$", name_part) is None`.

Common situations: A project name with unusual characters; a misescaped double dash; filenames produced by a non-compliant or outdated build tool; manual edits to the wheel filename.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/028e80a25aff2c0e.json. Report an issue: GitHub.