pypa/pip · error · InvalidName
name is invalid: {name!r}
Error message
name is invalid: {name!r} What it means
Raised by `canonicalize_name(name, *, validate=True)` when `name` does not match the distribution-name regex `^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$` (single alphanumeric char, or alphanumeric start+end with `[a-z0-9._-]` in between). This is the Core-metadata 'Name' spec; a failing value is not a legal PyPI project name. Note `validate` defaults to `False`, so you only hit this by opting in.
Source
Thrown at src/pip/_vendor/packaging/utils.py:92
If **validate** is true, then the function will check if **name** is a valid
distribution name before normalizing.
:param str name: The name to normalize.
:param bool validate: Check whether the name is a valid distribution name.
:raises InvalidName: If **validate** is true and the name is not an
acceptable distribution name.
>>> from packaging.utils import canonicalize_name
>>> canonicalize_name("Django")
'django'
>>> canonicalize_name("oslo.concurrency")
'oslo-concurrency'
>>> canonicalize_name("requests")
'requests'
"""
if validate and not _validate_regex.fullmatch(name):
raise InvalidName(f"name is invalid: {name!r}")
# Ensure all ``.`` and ``_`` are ``-``
# Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503
# Much faster than re, and even faster than str.translate
value = name.lower().replace("_", "-").replace(".", "-")
# Condense repeats (faster than regex)
while "--" in value:
value = value.replace("--", "-")
return cast("NormalizedName", value)
def is_normalized_name(name: str) -> bool:
"""
Check if a name is already normalized (i.e. :func:`canonicalize_name` would
roundtrip to the same value).
:param str name: The name to check.
>>> from packaging.utils import is_normalized_nameView on GitHub (pinned to d7d0d0a394)
Solutions
- Sanitize/trim the name before calling: strip, remove leading/trailing `._-`, ensure at least one alphanumeric character.
- Drop the `validate=True` flag if you only need normalization and tolerate legacy names.
- Fix the upstream source of the name (config file, CLI arg, metadata).
- Pre-check with the same regex before calling so you can branch on invalid input.
Example fix
// before
from pip._vendor.packaging.utils import canonicalize_name
canonicalize_name(name.strip(), validate=True) # raises if name is '.foo'
# after
import re
_NAME_RE = re.compile(r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII)
if not _NAME_RE.fullmatch(name):
raise ValueError(f"refusing to canonicalize invalid name: {name!r}
clean = canonicalize_name(name, validate=True) Defensive patterns
Strategy: validation
Validate before calling
import re
_NAME_RE = re.compile(r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII)
def is_valid_name(name: str) -> bool:
return bool(_NAME_RE.fullmatch(name)) Type guard
import re
_NAME_RE = re.compile(r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII)
def is_valid_distribution_name(name: object) -> bool:
return isinstance(name, str) and bool(_NAME_RE.fullmatch(name)) Try / catch
from pip._vendor.packaging.utils import canonicalize_name, InvalidName
try:
norm = canonicalize_name(raw, validate=True)
except InvalidName:
norm = canonicalize_name(raw.strip().strip('._-'), validate=False) Prevention
- Sanitize names (strip, remove leading/trailing `._-`) before validating.
- Only opt into `validate=True` when the name comes from untrusted/user input.
- Pre-check with the distribution-name regex to give actionable errors.
When it happens
Trigger: Calling `canonicalize_name('', validate=True)`, `canonicalize_name('.dotted', validate=True)`, names with leading/trailing dots/underscores/hyphens, empty string, or names containing whitespace or non-ASCII punctuation. The check is `_validate_regex.fullmatch(name)` is falsy.
Common situations: User-supplied or config-derived package names that were never sanitized; names with a trailing `-` from a sloppy split; empty name after stripping; non-ASCII characters; mixing `validate=True` into a previously permissive code path during an upgrade.
Related errors
- Invalid project name: {filename!r}
- Invalid wheel filename (compressed tag set components must b
- Invalid specification '%s'
- Ill-formed name/version string: '%s'
- Invalid wheel filename (extension must be '.whl'): {filename
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/375188ad72aab7df.json.
Report an issue: GitHub.