pypa/pip · error · InvalidVersion
Invalid version: {version!r}
Error message
Invalid version: {version!r} What it means
Raised by `Version.__init__` on the fast path: when the version string consists only of digits and dots (`_SIMPLE_VERSION_INDICATORS.issuperset(version)`) but contains an empty component when split on `.`, e.g. `1..2`, `.1`, `1.`, or `''`. Empty release components are not valid PEP 440, so the parser raises `InvalidVersion` with `from None` to suppress the inner `ValueError`.
Source
Thrown at src/pip/_vendor/packaging/version.py:422
def __init__(self, version: str) -> None:
"""Initialize a Version object.
:param version:
The string representation of a version which will be parsed and normalized
before use.
:raises InvalidVersion:
If the ``version`` does not conform to PEP 440 in any way then this
exception will be raised.
"""
if _SIMPLE_VERSION_INDICATORS.issuperset(version):
try:
self._release = tuple(map(int, version.split(".")))
except ValueError:
# Empty parts (from "1..2", ".1", etc.) are invalid versions.
# Any other ValueError (e.g. int str-digits limit) should
# propagate to the caller.
if "" in version.split("."):
raise InvalidVersion(f"Invalid version: {version!r}") from None
# TODO: remove "no cover" when Python 3.9 is dropped.
raise # pragma: no cover
self._epoch = 0
self._pre = None
self._post = None
self._dev = None
self._local = None
self._key_cache = None
self._hash_cache = None
return
# Validate the version and parse it into pieces
match = self._regex.fullmatch(version)
if not match:
raise InvalidVersion(f"Invalid version: {version!r}")
self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
self._release = tuple(map(int, match.group("release").split(".")))View on GitHub (pinned to d7d0d0a394)
Solutions
- Validate/normalize the version string before constructing: reject empty parts.
- Fix the producer so trailing/double dots are not emitted.
- Use `canonicalize_version(s)` or pre-check `'..' not in s and not s.strip('.') and ...`.
- Prefer building from structured data via `Version.from_parts(release=(1, 2))`.
Example fix
// before
Version(f"{major}.{minor}.") # trailing dot -> '' part -> raises
// after
parts = [str(x) for x in (major, minor) if x is not None]
Version('.'.join(parts))
# or
Version.from_parts(release=(major, minor)) Defensive patterns
Strategy: try-catch
Validate before calling
def is_simple_valid_version(s: str) -> bool:
# Fast-path rejection: only digits and dots, no empty parts
if not s or not set(s).issubset('.0123456789'):
return False
return '' not in s.split('.') Type guard
def has_no_empty_release_parts(s: object) -> bool:
if not isinstance(s, str) or not s:
return False
if not set(s).issubset('.0123456789'):
return True # not the fast path; defer to full validation
return '' not in s.split('.') Try / catch
from pip._vendor.packaging.version import Version, InvalidVersion
try:
v = Version(version_str)
except InvalidVersion:
# normalize: drop empty parts, then retry or surface a clear error
cleaned = '.'.join(p for p in version_str.split('.') if p)
v = Version(cleaned) Prevention
- Strip trailing/double dots from string-built versions before constructing `Version`.
- Build versions from structured data via `Version.from_parts(release=(...))` instead of string concatenation.
- Pre-check that `split('.')` contains no empty strings.
When it happens
Trigger: `Version('1..2')`, `Version('.1')`, `Version('1.')`, `Version('')`, `Version('1.2.')`. Any all-digits-and-dots string where `version.split('.')` contains an empty string.
Common situations: String-built versions with a trailing dot from `f"{major}.{minor}."`; concatenation producing `..`; an empty string after stripping; user input not trimmed.
Related errors
- epoch must be non-negative integer, got {epoch}
- release must be a non-empty tuple of non-negative integers,
- pre must be a tuple of ('a'|'b'|'rc', non-negative int), got
- post must be non-negative integer, got {value}
- dev must be non-negative integer, got {value}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/ccec0f14437bcae9.json.
Report an issue: GitHub.