pypa/pip · error · ValueError

set relations do not support === specifiers

Error message

set relations do not support === specifiers

What it means

Raised as ValueError by SpecifierSet._check_relation_operand (specifiers.py:1147) when either operand of a set relation (is_subset/is_superset/etc.) contains an arbitrary-equality (===) specifier. The _has_arbitrary flag (set during construction from any '===' token) makes set relations mathematically undefined, because === does literal string matching rather than version-range admission, so subset/superset reasoning does not apply.

Source

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

        """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

        .. versionadded:: 26.3
        """

View on GitHub (pinned to f399c37189)

Solutions

  1. Filter out or convert === specifiers before computing set relations, or skip the relation for sets that use them.
  2. Detect _has_arbitrary (or '===' in str(ss)) and report that comparison is unsupported for arbitrary pins.
  3. Replace the === pin with an == pin if exact version admission (rather than literal string equality) is intended, then re-run the relation.
  4. Restructure to compare via contains() per-candidate instead of subset/superset when === is involved.

Example fix

# before
a = SpecifierSet('===1.2.3')
b = SpecifierSet('>=1.0')
a.is_subset(b)  # ValueError: set relations do not support === specifiers

# after - guard and skip, or convert to ==
if '===' in str(a) or '===' in str(b):
    raise NotImplementedError('subset not defined for === pins')
# or: a = SpecifierSet('==1.2.3')  # if exact version admission is meant
Defensive patterns

Strategy: validation

Validate before calling

from packaging.specifiers import SpecifierSet

def safe_subset(a: SpecifierSet, b: SpecifierSet):
    if '===' in str(a) or '===' in str(b):
        raise NotImplementedError('set relations unsupported with === specifiers')
    return a.is_subset(b)

Type guard

from packaging.specifiers import SpecifierSet
def has_no_arbitrary(ss: SpecifierSet) -> bool:
    return not ss._has_arbitrary

Try / catch

try:
    a.is_subset(b)
except ValueError:
    # fall back to per-candidate contains() checks
    ...

Prevention

When it happens

Trigger: SpecifierSet('===1.0').is_subset(SpecifierSet('>=1.0')); either side built from a requirement that uses '===' (e.g. a local/version-locked pin '===1.0.0+local'); combining a frozen-pin requirement with a range-based policy check.

Common situations: A lock file or resolver that emits '===' pins being fed into a dependency-overlap analyzer using is_subset; tools that compare 'compatible ranges' across packages where one package uses arbitrary equality; PEP 440 edge-case testing.

Related errors


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