pypa/pip · error · InvalidVersion
epoch must be non-negative integer, got
Error message
epoch must be non-negative integer, got {epoch} What it means
Raised by _validate_epoch() (version.py:265-270) when the epoch value passed to Version.from_parts() or Version.__replace__() is not a non-negative integer. The epoch defaults to 0 if falsy, but must otherwise be an int >= 0. The exception class is InvalidVersion (a ValueError subclass).
Solutions
- Pass epoch as a non-negative int (0, 1, 2, ...).
- Convert string epochs with int(epoch) and validate >= 0 before calling from_parts().
- Omit the epoch argument entirely if you want the default of 0.
Example fix
// before Version.from_parts(epoch='2', release=(1, 0)) // after Version.from_parts(epoch=2, release=(1, 0))
Defensive patterns
Strategy: type-guard
Validate before calling
from packaging.version import Version, InvalidVersion
def safe_from_parts(*, release, epoch=0, **kwargs):
if not isinstance(epoch, int) or epoch < 0:
raise TypeError(f"epoch must be a non-negative int, got {epoch!r}")
return Version.from_parts(epoch=epoch, release=release, **kwargs) Type guard
def is_valid_epoch(value) -> bool:
return (value is None or value == 0) or (isinstance(value, int) and value >= 0) Try / catch
from packaging.version import InvalidVersion
try:
v = Version.from_parts(epoch=epoch, release=(1,))
except InvalidVersion as e:
if "epoch" in str(e):
logger.error("Invalid epoch value: %s", epoch)
raise Prevention
- Always pass epoch as a non-negative int
- Convert string epochs with int() before calling
- Omit the epoch argument to use the default of 0
When it happens
Trigger: Calling Version.from_parts(epoch=-1, release=(1,)) or Version.from_parts(epoch=1.5, release=(1,)) or Version.from_parts(epoch='1', release=(1,)). A string, float, or negative int triggers this.
Common situations: Passing an epoch as a string from a config file, a negative epoch due to a computation error, or a float epoch from division.
Related errors
- dev must be non-negative integer, got
- Invalid sdist filename (invalid version)
- Invalid version
- Invalid wheel filename (invalid version)
- local must be a valid version string, got
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/335c6d1a2bb42f06.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/version.py:270
:meta hide-value:
"""
# Validation pattern for local version in replace()
_LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE | re.ASCII)
# Fast path: If a version has only digits and dots then we
# can skip the regex and parse it as a release segment
_SIMPLE_VERSION_INDICATORS = frozenset(".0123456789")
def _validate_epoch(value: object, /) -> int:
epoch = value or 0
if isinstance(epoch, int) and epoch >= 0:
return epoch
msg = f"epoch must be non-negative integer, got {epoch}"
raise InvalidVersion(msg)
def _validate_release(value: object, /) -> tuple[int, ...]:
release = (0,) if value is None else value
if (
isinstance(release, tuple)
and len(release) > 0
and all(isinstance(i, int) and i >= 0 for i in release)
):
return release
msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
raise InvalidVersion(msg)
def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
if value is None:
return value
if isinstance(value, tuple) and len(value) == 2:View on GitHub (pinned to f399c37189)