pypa/pip · error · ValueError
Cannot combine VersionRange operands with different pre-rele
Error message
Cannot combine VersionRange operands with different pre-release policies: {self._prereleases_configured!r} and {other._prereleases_configured!r} What it means
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.
Source
Thrown at src/pip/_vendor/packaging/ranges.py:1089
"""
return self._admit_arbitrary and self._bounds == FULL_RANGE
def _is_plain(self) -> bool:
"""True when membership is decided by ``_bounds`` alone, enabling the
bounds-only fast paths in :meth:`is_subset` and :meth:`is_disjoint`.
"""
return (
not self._has_literals()
and not self._admit_arbitrary
and self._prereleases_configured is not False
)
def _check_policy_compat(self, other: VersionRange) -> None:
"""Refuse combining ranges with different pre-release policies."""
if not isinstance(other, VersionRange):
raise TypeError(f"expected VersionRange, got {type(other).__name__}")
if self._prereleases_configured != other._prereleases_configured:
raise ValueError(
"Cannot combine VersionRange operands with different "
f"pre-release policies: {self._prereleases_configured!r} "
f"and {other._prereleases_configured!r}"
)
def _merged_region(self, other: VersionRange) -> tuple[Interval, ...]:
"""Union of ``self`` and ``other``'s opt-in regions.
Used by :meth:`union` and :meth:`intersection`; :meth:`_build` clips the
merge to the result bounds. A configured operand carries an empty region,
so it contributes nothing to the merge.
"""
# Reuse an operand's canonical tuple when only one side has a region;
# an empty side contributes nothing to the union.
if not other._pre_region:
return self._pre_region
if not self._pre_region:
return other._pre_regionView on GitHub (pinned to f399c37189)
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.
Example fix
# before
a = SpecifierSet('>=1.0', prereleases=True).to_range()
b = SpecifierSet('<2.0', prereleases=False).to_range()
combined = a.union(b) # ValueError
# after
a = SpecifierSet('>=1.0', prereleases=True).to_range()
b = SpecifierSet('<2.0', prereleases=True).to_range()
combined = a.union(b) Defensive patterns
Strategy: validation
Validate before calling
from packaging.ranges import VersionRange
from packaging.specifiers import SpecifierSet
def same_prerelease_policy(a: VersionRange, b: VersionRange) -> bool:
return a._prereleases_configured == b._prereleases_configured
def safe_combine(a, b, op):
if not same_prerelease_policy(a, b):
# reconcile: rebuild b from its spec without an override, or match a's policy
b = SpecifierSet(str(b)).to_range() # default policy
return op(a, b) Type guard
null
Try / catch
try:
combined = a.union(b)
except ValueError as e:
# differing prereleases policies; rebuild operands with matching/None policy
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- expected VersionRange, got {type(other).__name__}
- cannot create 'VersionRange' instances directly; use Specifi
- VersionRange.contains() expected str or Version, got {type(i
- Cannot combine SpecifierSets with True and False prerelease
- invalid-egg-fragment
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/ae2e31888e064de6.
Report an issue: GitHub.