{"record":{"id":"51ba88e4366f8910","repo":"pypa/pip","slug":"expected-versionrange-got-type-other-name","errorCode":null,"errorMessage":"expected VersionRange, got {type(other).__name__}","messagePattern":"expected VersionRange, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/ranges.py","lineNumber":1087,"sourceCode":"        away from full bounds it survives only on empty-bounds ranges, where\n        it keeps ``~~full() == full()`` and union idempotent.\n        \"\"\"\n        return self._admit_arbitrary and self._bounds == FULL_RANGE\n\n    def _is_plain(self) -> bool:\n        \"\"\"True when membership is decided by ``_bounds`` alone, enabling the\n        bounds-only fast paths in :meth:`is_subset` and :meth:`is_disjoint`.\n        \"\"\"\n        return (\n            not self._has_literals()\n            and not self._admit_arbitrary\n            and self._prereleases_configured is not False\n        )\n\n    def _check_policy_compat(self, other: VersionRange) -> None:\n        \"\"\"Refuse combining ranges with different pre-release policies.\"\"\"\n        if not isinstance(other, VersionRange):\n            raise TypeError(f\"expected VersionRange, got {type(other).__name__}\")\n        if self._prereleases_configured != other._prereleases_configured:\n            raise ValueError(\n                \"Cannot combine VersionRange operands with different \"\n                f\"pre-release policies: {self._prereleases_configured!r} \"\n                f\"and {other._prereleases_configured!r}\"\n            )\n\n    def _merged_region(self, other: VersionRange) -> tuple[Interval, ...]:\n        \"\"\"Union of ``self`` and ``other``'s opt-in regions.\n\n        Used by :meth:`union` and :meth:`intersection`; :meth:`_build` clips the\n        merge to the result bounds. A configured operand carries an empty region,\n        so it contributes nothing to the merge.\n        \"\"\"\n        # Reuse an operand's canonical tuple when only one side has a region;\n        # an empty side contributes nothing to the union.\n        if not other._pre_region:\n            return self._pre_region","sourceCodeStart":1069,"sourceCodeEnd":1105,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/ranges.py#L1069-L1105","documentation":"TypeError raised by VersionRange._check_policy_compat (invoked by union, intersection, difference, issubset, isdisjoint, etc.) when the 'other' operand is not a VersionRange. VersionRange set-algebra is only defined between two VersionRange instances; mixing in a SpecifierSet, str, or set is a type error.","triggerScenarios":"Calling e.g. range_a.union(specifierset) or range_a.intersection('>=1.0') — passing anything that is not an instance of VersionRange. The guard 'isinstance(other, VersionRange)' fails and raises with the actual type name.","commonSituations":"Refactoring code that previously combined SpecifierSets directly; passing a specifier string assuming implicit conversion; a generic helper that accepts both types and forgets to convert.","solutions":["Convert the non-range operand to a VersionRange first: SpecifierSet(s).to_range() for a specifier, VersionRange.singleton(v) for a single version.","If you hold a SpecifierSet, call .to_range() once and reuse the resulting VersionRange in all set operations.","Type-narrow inputs in shared helpers so only VersionRange reaches the set-algebra methods.","If the operand is a plain string meant as an exact match, use VersionRange.singleton(string)."],"exampleFix":"# before\nfrom packaging.specifiers import SpecifierSet\nr = SpecifierSet('>=1.0').to_range()\ncombined = r.union(SpecifierSet('<2.0'))  # TypeError\n\n# after\ncombined = r.union(SpecifierSet('<2.0').to_range())","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"from packaging.ranges import VersionRange\nfrom packaging.specifiers import SpecifierSet\n\ndef as_version_range(x) -> VersionRange:\n    if isinstance(x, VersionRange):\n        return x\n    if isinstance(x, SpecifierSet):\n        return x.to_range()\n    if isinstance(x, str):\n        return SpecifierSet(x).to_range()\n    raise TypeError(f\"expected VersionRange/SpecifierSet/str, got {type(x).__name__}\")\n\n# then: a.union(as_version_range(other))","tryCatchPattern":"try:\n    combined = a.union(b)\nexcept TypeError as e:\n    # convert b to a VersionRange via as_version_range() and retry\n    raise","preventionTips":["Convert every SpecifierSet to a VersionRange once and pass ranges around thereafter.","Type-narrow inputs to set-algebra helpers so only VersionRange reaches union/intersection/etc.","Do not pass specifier strings directly to set operations; build a range first."],"tags":["packaging","versionrange","type-error","set-algebra"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}