pypa/pip · error · InvalidVersion

release must be a non-empty tuple of non-negative integers…

Error message

release must be a non-empty tuple of non-negative integers, got {release}

What it means

Raised by _validate_release() (version.py:273-282) when the release value passed to Version.from_parts() or Version.__replace__() is not a non-empty tuple of non-negative integers. None defaults to (0,), but any other value must be a tuple of ints where each element >= 0 and len > 0. Lists, strings, empty tuples, or tuples with negative/float/string elements all trigger this.

Solutions

  1. Pass release as a non-empty tuple of non-negative ints, e.g. (1, 2, 3).
  2. Convert lists with tuple(lst) and ensure all elements are int >= 0 before calling.
  3. Provide at least one release component; use (0,) for a zero version.

Example fix

// before
Version.from_parts(release=[1, 0])
// after
Version.from_parts(release=(1, 0))
Defensive patterns

Strategy: type-guard

Validate before calling

from packaging.version import Version, InvalidVersion

def safe_from_parts(*, release, **kwargs):
    if not isinstance(release, tuple) or len(release) == 0:
        raise TypeError("release must be a non-empty tuple")
    if not all(isinstance(i, int) and i >= 0 for i in release):
        raise TypeError("release must contain only non-negative ints")
    return Version.from_parts(release=release, **kwargs)

Type guard

def is_valid_release(value) -> bool:
    return (
        isinstance(value, tuple)
        and len(value) > 0
        and all(isinstance(i, int) and i >= 0 for i in value)
    )

Try / catch

from packaging.version import InvalidVersion

try:
    v = Version.from_parts(release=release)
except InvalidVersion as e:
    if "release" in str(e):
        logger.error("Invalid release value: %s", release)
    raise

Prevention

When it happens

Trigger: Calling Version.from_parts(release=[1, 0]) (list not tuple), Version.from_parts(release=()) (empty), Version.from_parts(release=(1, -1)) (negative), or Version.from_parts(release='1.0') (string).

Common situations: Passing a list instead of a tuple, an empty release, or release components read as strings from JSON/config that were not converted.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/be98a9c29c89431a. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_vendor/packaging/version.py:282

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:
        letter, number = value
        # The letter must be a string before it can be normalized.
        if (
            isinstance(letter, str)
            and (normalized := normalize_pre(letter)) in {"a", "b", "rc"}
            and isinstance(number, int)
            and number >= 0
        ):
            # type checkers can't infer the Literal type here on letter
            return (normalized, number)  # type: ignore[return-value]
    msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
    raise InvalidVersion(msg)

View on GitHub (pinned to f399c37189)