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
- Ensure the build tag, when present, begins with a digit (e.g. `1`, `1abc`, `2final`).
- Omit the build tag entirely if you don't need it (use a 5-part, 4-dash filename).
- Rebuild with a compliant backend that emits build tags correctly.
- 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 adding a build tag, always start it with a digit (`1`, `1abc`).
- Prefer omitting the build tag unless you need it.
- Use a current backend that emits build tags per PEP 427.
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
- Invalid wheel filename (extension must be '.whl'): {filename
- Invalid wheel filename (wrong number of parts): {filename!r}
- Invalid project name: {filename!r}
- Invalid wheel filename (invalid version): {filename!r}
- Invalid wheel filename (compressed tag set components must b
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/d332fe76a25b1dee.json.
Report an issue: GitHub.