pypa/pip · error · InvalidWheelFilename

Invalid build number: {build_part} in {filename!r}

Error message

Invalid build number: {build_part} in {filename!r}

What it means

Raised by `parse_wheel_filename` when a build tag is present (the filename has 5 dashes, indicating 6 parts) but the build segment does not match `_build_tag_regex = r'(\d+)(.*)'` — i.e. it does not start with one or more digits. PEP 427 requires the build number to begin with a digit, optionally followed by any other characters.

Source

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

    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:
        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
    return (name, version, build, tags)


def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
    """

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Ensure the build tag, when present, begins with a digit (e.g. `1`, `1abc`, `2final`).
  2. Omit the build tag entirely if you don't need it (use a 5-part, 4-dash filename).
  3. Rebuild with a compliant backend that emits build tags correctly.
  4. Rename the file to a build tag matching `^\d+.*$`.

Example fix

// before
parse_wheel_filename('foo-1.0-abc-py3-none-any.whl')  # build 'abc' has no leading digit

# after
parse_wheel_filename('foo-1.0-1abc-py3-none-any.whl')
Defensive patterns

Strategy: validation

Validate before calling

import re
_BUILD_RE = re.compile(r'(\d+)(.*)', re.ASCII)

def has_valid_build_tag(filename: str) -> bool:
    if not filename.endswith('.whl'):
        return True
    stem = filename[:-4]
    if stem.count('-') != 5:
        return True  # no build tag present
    # build tag is the 3rd segment after splitting on first/last 2 dashes
    parts = stem.split('-', stem.count('-') - 2)
    return bool(_BUILD_RE.match(parts[2]))

Try / catch

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

try:
    parse_wheel_filename(filename)
except InvalidWheelFilename as e:
    if 'Invalid build number' in str(e):
        # make the build tag start with a digit, or drop it
        ...
    raise

Prevention

When it happens

Trigger: A wheel filename like `foo-1.0-abc1-py3-none-any.whl` where the build tag `abc1` does not start with a digit; or `foo-1.0--py3-none-any.whl` (empty build segment).

Common situations: Hand-built wheels with a custom build tag; an old/non-compliant backend; misinterpretation of the optional build-tag slot.

Related errors


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