{"record":{"id":"d45312f6908f5329","repo":"pypa/pip","slug":"pre-must-be-a-tuple-of-a-b-rc-non-negative","errorCode":null,"errorMessage":"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}","messagePattern":"pre must be a tuple of \\('a'\\|'b'\\|'rc', non-negative int\\), got (.+?)","errorType":"validation","errorClass":"InvalidVersion","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/version.py","lineNumber":300,"sourceCode":"    raise InvalidVersion(msg)\n\n\ndef _validate_pre(value: object, /) -> tuple[Literal[\"a\", \"b\", \"rc\"], int] | None:\n    if value is None:\n        return value\n    if isinstance(value, tuple) and len(value) == 2:\n        letter, number = value\n        # The letter must be a string before it can be normalized.\n        if (\n            isinstance(letter, str)\n            and (normalized := normalize_pre(letter)) in {\"a\", \"b\", \"rc\"}\n            and isinstance(number, int)\n            and number >= 0\n        ):\n            # type checkers can't infer the Literal type here on letter\n            return (normalized, number)  # type: ignore[return-value]\n    msg = f\"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}\"\n    raise InvalidVersion(msg)\n\n\ndef _validate_post(value: object, /) -> tuple[Literal[\"post\"], int] | None:\n    if value is None:\n        return value\n    if isinstance(value, int) and value >= 0:\n        return (\"post\", value)\n    msg = f\"post must be non-negative integer, got {value}\"\n    raise InvalidVersion(msg)\n\n\ndef _validate_dev(value: object, /) -> tuple[Literal[\"dev\"], int] | None:\n    if value is None:\n        return value\n    if isinstance(value, int) and value >= 0:\n        return (\"dev\", value)\n    msg = f\"dev must be non-negative integer, got {value}\"\n    raise InvalidVersion(msg)","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/version.py#L282-L318","documentation":"Raised by _validate_pre() (version.py:285-300) when the pre-release value is not a 2-tuple whose first element normalizes (via normalize_pre) to 'a', 'b', or 'rc' and whose second element is a non-negative int. Valid spellings include 'alpha', 'a', 'beta', 'b', 'preview', 'pre', 'c', 'rc' for the letter. Passing None is allowed (means no pre-release).","triggerScenarios":"Calling Version.from_parts(release=(1,), pre=('gamma', 1)) (invalid letter), Version.from_parts(release=(1,), pre=('a', -1)) (negative number), Version.from_parts(release=(1,), pre=('a',)) (wrong length), or Version.from_parts(release=(1,), pre='a1') (string not tuple).","commonSituations":"Passing a pre-release string instead of a tuple, using an unrecognized pre-release label, or a negative pre-release number from a computation.","solutions":["Pass pre as a 2-tuple: ('a', 1), ('b', 2), ('rc', 1), or use accepted aliases like ('alpha', 1).","Ensure the second element is a non-negative int.","Pass None (or omit) if there is no pre-release segment."],"exampleFix":"// before\nVersion.from_parts(release=(1, 0), pre='alpha1')\n// after\nVersion.from_parts(release=(1, 0), pre=('alpha', 1))","handlingStrategy":"type-guard","validationCode":"from packaging.version import Version, InvalidVersion, normalize_pre\n\ndef safe_from_parts(*, release, pre=None, **kwargs):\n    if pre is not None:\n        if not (isinstance(pre, tuple) and len(pre) == 2):\n            raise TypeError(f\"pre must be a 2-tuple, got {pre!r}\")\n        letter, number = pre\n        if normalize_pre(letter) not in {'a', 'b', 'rc'}:\n            raise ValueError(f\"pre letter must normalize to a/b/rc, got {letter!r}\")\n        if not isinstance(number, int) or number < 0:\n            raise TypeError(f\"pre number must be non-negative int, got {number!r}\")\n    return Version.from_parts(release=release, pre=pre, **kwargs)","typeGuard":"from packaging.version import normalize_pre\n\ndef is_valid_pre(value) -> bool:\n    if value is None:\n        return True\n    return (\n        isinstance(value, tuple)\n        and len(value) == 2\n        and isinstance(value[0], str)\n        and normalize_pre(value[0]) in {'a', 'b', 'rc'}\n        and isinstance(value[1], int)\n        and value[1] >= 0\n    )","tryCatchPattern":"from packaging.version import InvalidVersion\n\ntry:\n    v = Version.from_parts(release=(1,), pre=pre)\nexcept InvalidVersion as e:\n    if \"pre must be\" in str(e):\n        logger.error(\"Invalid pre-release value: %s\", pre)\n    raise","preventionTips":["Pass pre as a 2-tuple like ('a', 1) or ('alpha', 2)","Use normalize_pre() to validate the letter before calling","Pass None to omit the pre-release segment"],"tags":["version","pre-release","packaging","pep440"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}