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

  1. For list containment: s.list.contains(1) instead of s == 1
  2. For struct fields: compare the field via s.struct.field("name") == value
  3. Use expressions: df.select(pl.col("col") == value) or filter with the appropriate expr (list/struct namespaces)
  4. 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

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


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/341045e2b13207a2. Report an issue: GitHub.