pypa/pip · error · TypeError
VersionRange.contains() expected str or Version, got {type(i
Error message
VersionRange.contains() expected str or Version, got {type(item).__name__} What it means
TypeError raised by VersionRange.contains when the 'item' argument is neither a str nor a packaging.version.Version. Membership testing requires something parseable as a version (or already a Version object); other types are rejected upfront rather than coerced.
Source
Thrown at src/pip/_vendor/packaging/ranges.py:1938
opt-in region; it reads only the configured policy. This mirrors
:meth:`~packaging.specifiers.SpecifierSet.contains` versus
:meth:`~packaging.specifiers.SpecifierSet.filter`.
Unparsable strings do not match, except where the full
``SpecifierSet`` would also match: the full range admits any string,
and a ``===`` range admits items equal to the literal
case-insensitively.
>>> r = SpecifierSet(">=1.0,<2.0").to_range()
>>> r.contains("1.5")
True
>>> r.contains("2.0")
False
:raises TypeError: if item is not a str or Version.
"""
if not isinstance(item, (str, Version)):
raise TypeError(
f"VersionRange.contains() expected str or Version, "
f"got {type(item).__name__}"
)
parsed: Version | None = item if isinstance(item, Version) else None
if installed and parsed is None:
parsed = coerce_version(item)
if installed and parsed is not None and parsed.is_prerelease:
prereleases = True
effective_pre = (
self._prereleases_configured if prereleases is None else prereleases
)
if self._admit or self._reject:
item_str = str(item).lower()
if item_str in self._reject:
return FalseView on GitHub (pinned to f399c37189)
Solutions
- Convert the value to a string before testing: range.contains(str(value)).
- For numeric inputs, format deterministically (e.g. str(1.5) -> '1.5') or build a Version explicitly: Version('1.5').
- Validate/normalize incoming version data at your API boundary so only str or Version reaches contains().
- If you often have a Version already, pass it directly — that path avoids re-parsing.
Example fix
# before
r = SpecifierSet('>=1.0').to_range()
r.contains(1.5) # TypeError
# after
r = SpecifierSet('>=1.0').to_range()
r.contains('1.5')
# or
from packaging.version import Version
r.contains(Version('1.5')) Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
from packaging.version import Version
def coerce_for_contains(item):
if isinstance(item, (str, Version)):
return item
if isinstance(item, (int, float)):
return str(item)
raise TypeError(f"version membership needs str/Version, got {type(item).__name__}")
# then: r.contains(coerce_for_contains(value)) Try / catch
try:
ok = r.contains(value)
except TypeError:
# value was not str/Version; coerce via coerce_for_contains() and retry
raise Prevention
- Normalize version inputs to str at your API boundary; never pass raw int/float to contains().
- Where you already hold a Version, pass it to skip re-parsing.
- Guard user-supplied version fields with isinstance(x, (str, Version)) before testing membership.
When it happens
Trigger: Calling range.contains(123) (int), range.contains(1.5) (float), range.contains(None), or range.contains(some_other_object). Note: the 'in' operator (__contains__) forwards to contains with default args, so '1.5 in range_float_bug' where you passed a float also hits this.
Common situations: Passing a numeric version (int/float) instead of its string form; feeding a parsed Version from a different library; an unguarded user input that arrived as a non-string.
Related errors
- expected VersionRange, got {type(other).__name__}
- cannot create 'VersionRange' instances directly; use Specifi
- Cannot combine VersionRange operands with different pre-rele
- expected a SpecifierSet
- Invalid version: {version!r}
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/b15eb58452cdd5af.
Report an issue: GitHub.