nodejs/node · error · InvalidWheelFilename

Invalid wheel filename (wrong number of parts): {filename}

Error message

Invalid wheel filename (wrong number of parts): {filename}

What it means

InvalidWheelFilename raised when, after stripping '.whl', the stem does not contain exactly 4 or 5 dashes (the PEP 427 layout is name-version-(build)-python-abi-platform). 4 dashes = no build tag, 5 dashes = build tag present; anything else is structurally invalid.

Source

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

    # Local version segment
    if parsed.local is not None:
        parts.append(f"+{parsed.local}")

    return "".join(parts)


def parse_wheel_filename(
    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:

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Confirm the wheel was built by a standard PEP 517 backend (build, hatchling, setuptools) rather than renamed by hand.
  2. Ensure project names with dashes are escaped to underscores in the wheel filename (the backend does this automatically).
  3. Rebuild the wheel from source instead of editing the filename.

Example fix

# before (manually renamed, name dash not escaped)
# 'my-pkg-1.0-py3-none-any.whl'

# after (backend rebuild emits escaped name)
# 'my_pkg-1.0-py3-none-any.whl'
Defensive patterns

Strategy: validation

Validate before calling

def has_wheel_dash_count(filename: str) -> bool:
    if not filename.endswith('.whl'):
        return False
    return filename[:-4].count('-') in (4, 5)

Try / catch

try:
    name, ver, build, tags = parse_wheel_filename(fn)
except InvalidWheelFilename as e:
    if 'wrong number of parts' in str(e):
        pass  # log and skip non-conformant wheel

Prevention

When it happens

Trigger: A filename like 'foo-1.0-py3-none-any-extra-junk.whl' (6 dashes) or 'foo-py3-none-any.whl' (3 dashes, missing version), or any wheel whose name/version/tag components themselves contain stray dashes.

Common situations: Hand-renamed wheel files, names with unescaped dashes that should have been replaced with underscores per PEP 427, or wheels produced by non-compliant build tools.

Related errors


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