pypa/pip · error · TypeError

expected a SpecifierSet

Error message

expected a SpecifierSet

What it means

Raised as TypeError by SpecifierSet._check_relation_operand (specifiers.py:1145), the guard invoked by is_subset/is_superset/is_disjoint/is_proper_subset relations. If the other operand is not an instance of SpecifierSet the method raises immediately before any range comparison. Passing a raw string, a Specifier, a list, or None all trigger it.

Source

Thrown at src/pip/_vendor/packaging/specifiers.py:1145

    def to_range(self) -> ranges.VersionRange:
        """Return the :class:`~packaging.ranges.VersionRange` this set accepts.

        An empty set yields the full range; an unsatisfiable set yields the
        empty range. ``===`` specifiers contribute literal-string admission.

        >>> SpecifierSet(">=1.0,<2.0").to_range()
        <VersionRange '[1.0, 2.0.dev0)'>

        .. versionadded:: 26.3
        """
        from .ranges import VersionRange  # noqa: PLC0415

        return VersionRange._from_specifier_set(self)

    def _check_relation_operand(self, other: object) -> None:
        if not isinstance(other, SpecifierSet):
            raise TypeError("expected a SpecifierSet")
        if self._has_arbitrary or other._has_arbitrary:
            raise ValueError("set relations do not support === specifiers")

    def is_subset(self, other: SpecifierSet) -> bool:
        """Return whether every version matching this set also matches other.

        :raises ValueError:
            If either set uses ``===`` specifiers, or the two sets were
            given different ``prereleases`` arguments (unset on one side
            counts as different).
        :raises TypeError:
            If other is not a :class:`SpecifierSet`.

        >>> SpecifierSet(">=3.12,<3.13").is_subset(SpecifierSet(">=3.12"))
        True
        >>> SpecifierSet(">=3.12").is_subset(SpecifierSet(">=3.12,<3.13"))
        False

View on GitHub (pinned to f399c37189)

Solutions

  1. Wrap the operand: other = SpecifierSet(other) if isinstance(other, str) else other before calling the relation.
  2. Ensure your helper always passes a SpecifierSet instance; convert at the boundary.
  3. If other is already a single Specifier, wrap it: SpecifierSet(str(specifier)).
  4. Add a type guard / isinstance check and raise a clearer domain-specific error before the call.

Example fix

# before
ss = SpecifierSet('>=3.12')
ss.is_subset('>=3.12')  # TypeError: expected a SpecifierSet

# after
from packaging.specifiers import SpecifierSet
other = SpecifierSet('>=3.12') if isinstance(other, str) else other
ss.is_subset(other)
Defensive patterns

Strategy: type-guard

Validate before calling

from packaging.specifiers import SpecifierSet, Specifier

def to_specifierset(v) -> SpecifierSet:
    if isinstance(v, str):
        return SpecifierSet(v)
    if isinstance(v, Specifier):
        return SpecifierSet(str(v))
    if isinstance(v, SpecifierSet):
        return v
    raise TypeError(f'expected SpecifierSet/str/Specifier, got {type(v)!r}')

Type guard

from packaging.specifiers import SpecifierSet
def is_specifierset(v) -> bool:
    return isinstance(v, SpecifierSet)

Try / catch

try:
    a.is_subset(other)
except TypeError:
    other = to_specifierset(other)
    return a.is_subset(other)

Prevention

When it happens

Trigger: SpecifierSet('>=3.12').is_subset('>=3.12') (passing a str instead of SpecifierSet); .is_superset(some_specifier) (a Specifier, not a set); .is_subset(None); passing a list of spec strings.

Common situations: Calling the new 26.3 set-relation API with values read from config (strings) without wrapping them; mixing Specifier and SpecifierSet in a comparison helper; type-mismatched helper functions that accept 'specifier-like' input.

Related errors


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