{"record":{"id":"ae2e31888e064de6","repo":"pypa/pip","slug":"cannot-combine-versionrange-operands-with-differen","errorCode":null,"errorMessage":"Cannot combine VersionRange operands with different pre-release policies: {self._prereleases_configured!r} and {other._prereleases_configured!r}","messagePattern":"Cannot combine VersionRange operands with different pre-release policies: (.+?) and (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/ranges.py","lineNumber":1089,"sourceCode":"        \"\"\"\n        return self._admit_arbitrary and self._bounds == FULL_RANGE\n\n    def _is_plain(self) -> bool:\n        \"\"\"True when membership is decided by ``_bounds`` alone, enabling the\n        bounds-only fast paths in :meth:`is_subset` and :meth:`is_disjoint`.\n        \"\"\"\n        return (\n            not self._has_literals()\n            and not self._admit_arbitrary\n            and self._prereleases_configured is not False\n        )\n\n    def _check_policy_compat(self, other: VersionRange) -> None:\n        \"\"\"Refuse combining ranges with different pre-release policies.\"\"\"\n        if not isinstance(other, VersionRange):\n            raise TypeError(f\"expected VersionRange, got {type(other).__name__}\")\n        if self._prereleases_configured != other._prereleases_configured:\n            raise ValueError(\n                \"Cannot combine VersionRange operands with different \"\n                f\"pre-release policies: {self._prereleases_configured!r} \"\n                f\"and {other._prereleases_configured!r}\"\n            )\n\n    def _merged_region(self, other: VersionRange) -> tuple[Interval, ...]:\n        \"\"\"Union of ``self`` and ``other``'s opt-in regions.\n\n        Used by :meth:`union` and :meth:`intersection`; :meth:`_build` clips the\n        merge to the result bounds. A configured operand carries an empty region,\n        so it contributes nothing to the merge.\n        \"\"\"\n        # Reuse an operand's canonical tuple when only one side has a region;\n        # an empty side contributes nothing to the union.\n        if not other._pre_region:\n            return self._pre_region\n        if not self._pre_region:\n            return other._pre_region","sourceCodeStart":1071,"sourceCodeEnd":1107,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/ranges.py#L1071-L1107","documentation":"ValueError raised by VersionRange._check_policy_compat when both operands are VersionRange but their explicit pre-release policies (_prereleases_configured, i.e. an explicit True/False prereleases override on the originating SpecifierSet) differ. Combining them would silently change which pre-releases are admitted, so the library refuses and reports both policy values.","triggerScenarios":"Calling union/intersection/difference/etc. between ranges whose source SpecifierSets had different prereleases overrides, e.g. SpecifierSet('>=1.0', prereleases=True).to_range().union(SpecifierSet('<2.0', prereleases=False).to_range()). The values True/False/None must match.","commonSituations":"Mixing ranges derived from specifier sets configured with explicit prereleases=True/False in different parts of a resolver; a helper that builds ranges with a hardcoded override combined with user-supplied ranges that have None.","solutions":["Inspect each operand's _prereleases_configured (the values are shown in the message) and reconcile them.","Rebuild the originating SpecifierSets with the same explicit prereleases value (or None on both) before converting to ranges.","If one operand should govern the policy, drop the other's override (use None) and set it to match.","Where policy genuinely must differ, evaluate membership separately rather than combining the ranges."],"exampleFix":"# before\na = SpecifierSet('>=1.0', prereleases=True).to_range()\nb = SpecifierSet('<2.0', prereleases=False).to_range()\ncombined = a.union(b)  # ValueError\n\n# after\na = SpecifierSet('>=1.0', prereleases=True).to_range()\nb = SpecifierSet('<2.0', prereleases=True).to_range()\ncombined = a.union(b)","handlingStrategy":"validation","validationCode":"from packaging.ranges import VersionRange\nfrom packaging.specifiers import SpecifierSet\n\ndef same_prerelease_policy(a: VersionRange, b: VersionRange) -> bool:\n    return a._prereleases_configured == b._prereleases_configured\n\ndef safe_combine(a, b, op):\n    if not same_prerelease_policy(a, b):\n        # reconcile: rebuild b from its spec without an override, or match a's policy\n        b = SpecifierSet(str(b)).to_range()  # default policy\n    return op(a, b)","typeGuard":"null","tryCatchPattern":"try:\n    combined = a.union(b)\nexcept ValueError as e:\n    # differing prereleases policies; rebuild operands with matching/None policy\n    raise","preventionTips":["Standardize on one prereleases policy (ideally None) across specifier sets feeding your ranges.","Set prereleases explicitly on all originating SpecifierSets or on none of them.","Where policies must differ, evaluate membership separately instead of combining."],"tags":["packaging","versionrange","prereleases","set-algebra","policy"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}