pypa/pip · error · TypeError
cannot create 'VersionRange' instances directly; use Specifi
Error message
cannot create 'VersionRange' instances directly; use SpecifierSet.to_range(), VersionRange.full(), VersionRange.empty(), or VersionRange.singleton() instead
What it means
TypeError raised by VersionRange.__new__ because direct instantiation is intentionally blocked. VersionRange is an immutable value type with canonicalization invariants enforced by internal factories, so callers must construct it through the documented entry points: SpecifierSet.to_range(), VersionRange.full(), VersionRange.empty(), or VersionRange.singleton().
Source
Thrown at src/pip/_vendor/packaging/ranges.py:998
#: configured override). The opt-in flows only from the pre-release-naming
#: specifiers that built the range. :meth:`_build` clips the region to the
#: bounds, so it is always a subset of them: an opt-in that overflowed its
#: own cap cannot ride a later union into versions no specifier asked for.
#: :meth:`union` and :meth:`intersection` accumulate the operands' clipped
#: regions and re-clip to the result bounds; :meth:`difference` keeps only
#: the minuend's; and :meth:`complement` drops it, since an exclusion grants
#: no opt-in. Equality keys on the clipped region, so it stays a congruence.
_pre_region: tuple[Interval, ...]
#: Raw configured pre-release override of the originating specifier set
#: (an explicit ``True`` / ``False``, else ``None``). When set, :meth:`_build`
#: forces ``_pre_region`` empty since the policy governs globally.
#: :meth:`intersection` and :meth:`union` require it to match on both
#: operands. Part of equality.
_prereleases_configured: bool | None
def __new__(cls, *args: object, **kwargs: object) -> VersionRange: # noqa: PYI034
raise TypeError(
"cannot create 'VersionRange' instances directly; use "
"SpecifierSet.to_range(), VersionRange.full(), "
"VersionRange.empty(), or VersionRange.singleton() instead"
)
@classmethod
def _build(
cls,
bounds: tuple[Interval, ...],
admit: frozenset[str] = frozenset(),
reject: frozenset[str] = frozenset(),
admit_arbitrary: bool = False,
*,
pre_region: tuple[Interval, ...] = (),
prereleases_configured: bool | None = None,
) -> VersionRange:
"""Internal factory; bypasses :meth:`__new__`.
View on GitHub (pinned to f399c37189)
Solutions
- Replace VersionRange(...) with the appropriate factory: VersionRange.full() for all versions, VersionRange.empty() for none, VersionRange.singleton('1.0') for exactly one version.
- For a specifier-derived range, build a SpecifierSet first and call .to_range(): SpecifierSet('>=1.0,<2.0').to_range().
- If you need the union/intersection of existing ranges, use .union()/.intersection() on ranges you already have rather than constructing a new one directly.
- Check the docstring of the factory you choose for pre-release-policy semantics.
Example fix
# before
r = VersionRange('>=1.0,<2.0')
# after
from packaging.specifiers import SpecifierSet
r = SpecifierSet('>=1.0,<2.0').to_range()
# or for a single point / full / empty:
# VersionRange.singleton('1.0'), VersionRange.full(), VersionRange.empty() Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
# Reject direct construction at the boundary; only allow factory results.
from packaging.ranges import VersionRange
def is_version_range(x) -> bool:
return isinstance(x, VersionRange)
# construction helper that never calls VersionRange(...) directly
def make_range(spec_or_kind):
if isinstance(spec_or_kind, VersionRange):
return spec_or_kind
if spec_or_kind in ('full', 'empty'):
return VersionRange.full() if spec_or_kind == 'full' else VersionRange.empty()
from packaging.specifiers import SpecifierSet
return SpecifierSet(spec_or_kind).to_range() Try / catch
try:
r = make_range(spec)
except TypeError:
# called VersionRange(...) directly by mistake; route through a factory
raise Prevention
- Never call VersionRange(...); remember the four entry points: full, empty, singleton, SpecifierSet.to_range().
- Wrap range creation in a helper so all call sites go through factories.
- Type-hint helpers as VersionRange so misuse shows up in static analysis.
When it happens
Trigger: Calling VersionRange(...) directly, e.g. VersionRange() or VersionRange('>=1.0'). The overridden __new__ unconditionally raises before any arguments are considered.
Common situations: A developer guessing the API from the class name; migrating code that built a SpecifierSet and now wants a range; copy-pasting from a tutorial that predates the factory API.
Related errors
- expected VersionRange, got {type(other).__name__}
- Cannot combine VersionRange operands with different pre-rele
- VersionRange.contains() expected str or Version, got {type(i
- invalid-egg-fragment
- Invalid script entry point: {entry_point} - A callable suffi
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/d792701b63ae4c70.
Report an issue: GitHub.