nodejs/node · error · InvalidWheelFilename

Invalid project name: {filename}

Error message

Invalid project name: {filename}

What it means

InvalidWheelFilename raised when the name segment of the wheel filename is invalid per PEP 427 escaping rules: it contains '__' (the escape sequence for a literal dash) in an illegal position, or it fails the ^[\w\d._]*$ regex (e.g. contains spaces or other punctuation).

Source

Thrown at tools/gyp/pylib/packaging/utils.py:122

    filename: str,
) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
    if not filename.endswith(".whl"):
        raise InvalidWheelFilename(
            f"Invalid wheel filename (extension must be '.whl'): {filename}"
        )

    filename = filename[:-4]
    dashes = filename.count("-")
    if dashes not in (4, 5):
        raise InvalidWheelFilename(
            f"Invalid wheel filename (wrong number of parts): {filename}"
        )

    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}")
    name = canonicalize_name(name_part)

    try:
        version = Version(parts[1])
    except InvalidVersion as e:
        raise InvalidWheelFilename(
            f"Invalid wheel filename (invalid version): {filename}"
        ) 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}'"
            )
        build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
    else:

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Rebuild the wheel with a conformant backend so the name is escaped to ^[\w._]+$ with dashes encoded as '__'.
  2. Rename the project to use only ASCII letters, digits, underscores, and dots if escaping is the issue.
  3. Do not hand-edit wheel filenames; regenerate from sdist.

Example fix

# before (space in name part)
# 'my pkg-1.0-py3-none-any.whl'

# after (rebuilt with normalized name)
# 'my_pkg-1.0-py3-none-any.whl'
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try:
    parse_wheel_filename(fn)
except InvalidWheelFilename as e:
    if 'project name' in str(e):
        pass  # rebuild wheel with normalized name

Prevention

When it happens

Trigger: A wheel filename whose first dash-separated part (before the version) contains '__' inappropriately or non-word characters. This check runs after the dash-count check passes.

Common situations: Project names with unusual characters that the build backend failed to escape correctly, or filenames edited/constructed manually violating the PEP 427 name-escaping convention.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/fe8d64683ccc2f35. Report an issue: GitHub.