pypa/pip · error · InvalidWheelFilename

Invalid build number

Error message

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

What it means

Raised by parse_wheel_filename() when a build tag is present (5 dashes in the stem) but the build-segment string does not match _build_tag_regex (\d+)(.*) at utils.py:283-287. Per PEP 427, the build tag must start with a digit. If _build_tag_regex.match() returns None, the build number is invalid.

Solutions

  1. Ensure the build-tag segment starts with one or more digits (e.g. '1', '2final', '100abc').
  2. Remove the build tag entirely if you do not need one (go back to 4 dashes).
  3. Move non-numeric build metadata into the version's local segment instead.

Example fix

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

Strategy: validation

Validate before calling

import re
_build_tag_regex = re.compile(r"(\d+)(.*)", re.ASCII)

def safe_parse_wheel(filename: str):
    stem = filename[:-4]
    dashes = stem.count("-")
    if dashes == 5:
        build_part = stem.split("-", 3)[2]
        if _build_tag_regex.match(build_part) is None:
            raise ValueError(f"Build tag must start with a digit: {build_part!r}")
    return parse_wheel_filename(filename)

Type guard

import re

def has_valid_build_tag(filename: str) -> bool:
    if not isinstance(filename, str) or not filename.endswith(".whl"):
        return False
    stem = filename[:-4]
    if stem.count("-") != 5:
        return True  # no build tag present
    build_part = stem.split("-", 3)[2]
    return bool(re.match(r"\d+", build_part))

Try / catch

from packaging.utils import InvalidWheelFilename

try:
    parse_wheel_filename(filename)
except InvalidWheelFilename as e:
    if "Invalid build number" in str(e):
        logger.error("Wheel build tag must start with a digit: %s", filename)
    raise

Prevention

When it happens

Trigger: A wheel filename with 5 dashes where the build segment does not begin with a digit, e.g. 'foo-1.0-abc-py3-none-any.whl' where build part 'abc' fails the regex because it has no leading digit.

Common situations: A build backend that emits a build tag without a numeric prefix, or a manually renamed wheel file where a non-numeric token was placed in the build-tag position.

Related errors


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

Appendix: source

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

    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 _wheel_name_regex.match(name_part) 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
    except InvalidTag:
        raise InvalidWheelFilename(
            f"Invalid wheel filename (invalid tag component): {filename!r}"
        ) from None
    return (name, version, build, tags)

View on GitHub (pinned to f399c37189)