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

  1. Replace VersionRange(...) with the appropriate factory: VersionRange.full() for all versions, VersionRange.empty() for none, VersionRange.singleton('1.0') for exactly one version.
  2. For a specifier-derived range, build a SpecifierSet first and call .to_range(): SpecifierSet('>=1.0,<2.0').to_range().
  3. If you need the union/intersection of existing ranges, use .union()/.intersection() on ranges you already have rather than constructing a new one directly.
  4. 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

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


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