pypa/pip · error · InvalidWheelFilename
Invalid project name: {filename!r}
Error message
Invalid project name: {filename!r} What it means
Raised by `parse_wheel_filename` when the name segment of the wheel filename is itself invalid: it contains a literal `__` (double underscore, which is reserved for escaping runs of `_-` in project names) or does not match `^[\w\d._]*$` (alphanumerics, underscore, dot, hyphen). This guards PEP 427's name-escaping rules before canonicalization.
Source
Thrown at src/pip/_vendor/packaging/utils.py:218
The *validate_order* parameter.
"""
if not filename.endswith(".whl"):
raise InvalidWheelFilename(
f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
)
filename = filename[:-4]
dashes = filename.count("-")
if dashes not in (4, 5):
raise InvalidWheelFilename(
f"Invalid wheel filename (wrong number of parts): {filename!r}"
)
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:View on GitHub (pinned to d7d0d0a394)
Solutions
- Rebuild the wheel with a current, PEP 427-compliant backend that escapes project names correctly.
- Rename the name segment to use only `[A-Za-z0-9._-]` and no `__` runs.
- Validate the upstream distribution name (use `canonicalize_name(name, validate=True)`).
- If the artifact came from an external index, report/fallback and re-fetch a valid wheel.
Example fix
// before
parse_wheel_filename('foo__bar-1.0-py3-none-any.whl') # '__' in name -> raises
# after
parse_wheel_filename('foo_bar-1.0-py3-none-any.whl') Defensive patterns
Strategy: try-catch
Validate before calling
import re
def has_valid_wheel_name_segment(filename: str) -> bool:
if not filename.endswith('.whl'):
return False
stem = filename[:-4]
name_part = stem.split('-', stem.count('-') - 2)[0]
return '__' not in name_part and bool(re.match(r'^[\w\d._]*$', name_part, re.UNICODE)) Type guard
import re
def is_valid_wheel_name_segment(filename: object) -> bool:
if not isinstance(filename, str) or not filename.endswith('.whl'):
return False
stem = filename[:-4]
name_part = stem.split('-', max(1, stem.count('-') - 2))[0]
return '__' not in name_part and bool(re.match(r'^[\w\d._]*$', name_part, re.UNICODE)) Try / catch
from pip._vendor.packaging.utils import parse_wheel_filename, InvalidWheelFilename
try:
parse_wheel_filename(filename)
except InvalidWheelFilename as e:
if 'Invalid project name' in str(e):
# rename the name segment to alphanumerics + _ . - with no __
...
raise Prevention
- Ensure distribution names contain only `[A-Za-z0-9._-]` and no double-underscore runs.
- Let a compliant backend escape names; avoid manual renaming.
- Pre-validate the upstream name with `canonicalize_name(name, validate=True)`.
When it happens
Trigger: A wheel whose name segment contains `__` (e.g. from a name with consecutive `-`/`_` not properly escaped), or characters outside `[A-Za-z0-9_.-]` such as spaces, parentheses, or plus signs. The check is `'__' in name_part or re.match(r"^[\w\d._]*$", name_part) is None`.
Common situations: A project name with unusual characters; a misescaped double dash; filenames produced by a non-compliant or outdated build tool; manual edits to the wheel filename.
Related errors
- Invalid wheel filename (extension must be '.whl'): {filename
- Invalid wheel filename (wrong number of parts): {filename!r}
- Invalid build number: {build_part} in {filename!r}
- Invalid wheel filename (invalid version): {filename!r}
- name is invalid: {name!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/028e80a25aff2c0e.json.
Report an issue: GitHub.