pypa/pip · error · InvalidVersion
post must be non-negative integer, got {value}
Error message
post must be non-negative integer, got {value} What it means
Raised by `_validate_post(value)` when the `post` argument to `Version.from_parts`/`__replace__` is not `None` and not a non-negative `int`. The post-release number in PEP 440 (`1.0.post1`) is a single non-negative integer; floats, strings, lists, and negatives are rejected. The validator wraps the int into `('post', value)` internally.
Source
Thrown at src/pip/_vendor/packaging/version.py:305
if value is None:
return value
if isinstance(value, tuple) and len(value) == 2:
letter, number = value
letter = normalize_pre(letter)
if letter in {"a", "b", "rc"} and isinstance(number, int) and number >= 0:
# type checkers can't infer the Literal type here on letter
return (letter, number) # type: ignore[return-value]
msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
raise InvalidVersion(msg)
def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
if value is None:
return value
if isinstance(value, int) and value >= 0:
return ("post", value)
msg = f"post must be non-negative integer, got {value}"
raise InvalidVersion(msg)
def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
if value is None:
return value
if isinstance(value, int) and value >= 0:
return ("dev", value)
msg = f"dev must be non-negative integer, got {value}"
raise InvalidVersion(msg)
def _validate_local(value: object, /) -> LocalType | None:
if value is None:
return value
if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
return _parse_local_version(value)
msg = f"local must be a valid version string, got {value!r}"
raise InvalidVersion(msg)View on GitHub (pinned to d7d0d0a394)
Solutions
- Pass a non-negative `int`: `post=1`.
- Coerce with `int(value)` and assert `>= 0` first.
- For string input, use `Version('1.0.post1')`.
- Omit `post` (`None`) if there is no post-release segment.
Example fix
// before
Version.from_parts(release=(1, 0), post='1') # str -> raises
// after
Version.from_parts(release=(1, 0), post=int('1'))
# or
Version('1.0.post1') Defensive patterns
Strategy: type-guard
Validate before calling
def is_valid_post(value: object) -> bool:
return value is None or (isinstance(value, int) and not isinstance(value, bool) and value >= 0) Type guard
def is_non_negative_int_or_none(value: object) -> bool:
return value is None or (isinstance(value, int) and not isinstance(value, bool) and value >= 0) Try / catch
from pip._vendor.packaging.version import Version, InvalidVersion
try:
v = Version.from_parts(release=release, post=post)
except InvalidVersion as e:
if 'post' in str(e):
post = int(post)
v = Version.from_parts(release=release, post=post)
raise Prevention
- Pass `post` as a non-negative `int` or `None`.
- Coerce strings with `int()` and validate `>= 0` before calling.
- For string input, prefer `Version('1.0.post1')`.
When it happens
Trigger: `Version.from_parts(release=(1,0), post='1')`, `post=-1`, `post=1.5`, `post=[1]`. Check is `isinstance(value, int) and value >= 0`.
Common situations: Passing the post number as a string from config; a negative value from a buggy default; a float from arithmetic.
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
- dev must be non-negative integer, got {value}
- local must be a valid version string, got {value!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/efd9347ba1d420e6.json.
Report an issue: GitHub.