pola-rs/polars · error
Series of type {self.dtype} does not have {op} operator
Error message
Series of type {self.dtype} does not have {op} operator What it means
Raised in Series._comp (py-polars/src/polars/series/series.py:921) when no FFI comparison kernel exists for the Series dtype and the given scalar op. After the special scalar paths (datetime/time/float upcast/boolean/etc.) are exhausted, polars looks up a native eq/neq/gt/ge/lt/le kernel for self.dtype; nested dtypes (List/Array/Struct), Object, and some others have none, so the comparison raises NotImplementedError naming the dtype and operator.
Source
Thrown at py-polars/src/polars/series/series.py:921
assert f is not None
return self._from_pyseries(f(d))
if isinstance(other, Sequence) and not isinstance(other, str):
if self.dtype in (List, Array):
other = [other]
other = Series("", other)
if other.dtype == Null:
other.cast(self.dtype)
if isinstance(other, Series):
return self._from_pyseries(getattr(self._s, op)(other._s))
try:
f = get_ffi_func(op + "_<>", self.dtype, self._s)
except NotImplementedError:
f = None
if f is None:
msg = f"Series of type {self.dtype} does not have {op} operator"
raise NotImplementedError(msg)
if other is not None:
other = maybe_cast(other, self.dtype)
return self._from_pyseries(f(other))
@overload # type: ignore[override]
def __eq__(self, other: Expr) -> Expr: ... # type: ignore[overload-overlap]
@overload
def __eq__(self, other: object) -> Series: ...
def __eq__(self, other: object) -> Series | Expr:
warn_null_comparison(other)
if isinstance(other, pl.Expr):
return F.lit(self).__eq__(other)
return self._comp(other, "eq")
@overload # type: ignore[override]View on GitHub (pinned to df599052da)
Solutions
- For list containment: s.list.contains(1) instead of s == 1
- For struct fields: compare the field via s.struct.field("name") == value
- Use expressions: df.select(pl.col("col") == value) or filter with the appropriate expr (list/struct namespaces)
- Cast Object columns to a concrete dtype if possible before comparing
Example fix
# before s = pl.Series([[1, 2], [3]], dtype=pl.List(pl.Int64)) s == 1 # NotImplementedError # after s.list.contains(1) # elementwise: does each list contain 1?
Defensive patterns
Strategy: fallback
Validate before calling
import polars as pl
def compare_safe(s: pl.Series, value):
if s.dtype.base_type() in (pl.List, pl.Array, pl.Struct, pl.Object):
raise TypeError(f"no scalar comparison for {s.dtype}; use list/struct namespace")
return s == value Type guard
def has_scalar_comparison(s: pl.Series) -> bool:
return s.dtype.base_type() not in (pl.List, pl.Array, pl.Struct, pl.Object, pl.Null) Try / catch
try:
mask = s == value
except NotImplementedError as e:
if "does not have" in str(e) and s.dtype.base_type() in (pl.List, pl.Array):
mask = s.list.contains(value)
else:
raise Prevention
- Use s.list.contains(x) for list membership, s.struct.field(...) for struct fields
- Prefer expression context (df.filter(pl.col(...)...)) where nested comparisons are explicit
- Cast Object columns to concrete dtypes as soon as possible
When it happens
Trigger: pl.Series([[1, 2], [3]], dtype=pl.List(pl.Int64)) == 1 (scalar compare against a List Series); struct Series == something; Object-dtype Series == value. Note Series-to-Series comparison goes through a different branch, so s == pl.Series(...) may not hit this.
Common situations: Comparing nested columns to scalars expecting elementwise containment (a pandas-like reflex); testing Object columns; equality checks on struct columns. The right tool is usually a dtype-specific namespace or an expression.
Related errors
- datetime time zone {other.tzinfo!r} does not match Series ti
- cannot compare datetime.datetime to Series of type {self.dty
- cannot treat Series of type {s.dtype} as indices
- write_table: table format of {catalog_name}.{namespace}.{tab
- {how!r} strategy is not supported for {qualified_type_name(e
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/341045e2b13207a2.
Report an issue: GitHub.