pola-rs/polars · error

the truth value of a Series is ambiguous Here are some thin

Error message

the truth value of a Series is ambiguous

Here are some things you might want to try:
- instead of `if s`, use `if not s.is_empty()`
- instead of `s1 and s2`, use `s1 & s2`
- instead of `s1 or s2`, use `s1 | s2`
- instead of `s in [y, z]`, use `s.is_in([y, z])`

What it means

Series.__bool__ (py-polars/src/polars/series/series.py:752) deliberately raises TypeError because a Series has no single truth value: it is a collection of many elements, so `if s:` is ambiguous (length? all-true? any-true?). Python calls __bool__ for bool(s), `if s`, and the and/or operators, and polars rejects all of them with an actionable hint message.

Source

Thrown at py-polars/src/polars/series/series.py:752

        Examples
        --------
        >>> s = pl.Series("a", [1, 2, 3])
        >>> s.shape
        (3,)
        """
        return (self._s.len(),)

    def __bool__(self) -> NoReturn:
        msg = (
            "the truth value of a Series is ambiguous"
            "\n\n"
            "Here are some things you might want to try:\n"
            "- instead of `if s`, use `if not s.is_empty()`\n"
            "- instead of `s1 and s2`, use `s1 & s2`\n"
            "- instead of `s1 or s2`, use `s1 | s2`\n"
            "- instead of `s in [y, z]`, use `s.is_in([y, z])`\n"
        )
        raise TypeError(msg)

    def __getstate__(self) -> bytes:
        return self._s.__getstate__()

    def __setstate__(self, state: bytes) -> None:
        self._s = Series()._s  # Initialize with a dummy
        self._s.__setstate__(state)

    def __str__(self) -> str_:
        s_repr: str = self._s.as_str()
        return s_repr.replace("Series", f"{self.__class__.__name__}", 1)

    def __repr__(self) -> str_:
        return self.__str__()

    def __len__(self) -> int:
        return self.len()

View on GitHub (pinned to df599052da)

Solutions

  1. Emptiness: if s.is_empty(): ... (or `if not s.is_empty()`)
  2. Elementwise logic: use s1 & s2 (and), s1 | s2 (or), ~s (not); for reduction use s.all() / s.any()
  3. Membership: s.is_in([y, z]) instead of `s in [y, z]`
  4. For tests use assert_series_equal instead of assert

Example fix

# before
if s1 and s2: ...
if s in ["a", "b"]: ...

# after
if not (s1 & s2).is_empty(): ...
mask = s1 & s2  # elementwise 'and'
if s.is_in(["a", "b"]).any(): ...
Defensive patterns

Strategy: validation

Validate before calling

# Decide intent explicitly before branching on a Series:
if s.is_empty():          # instead of: if not s
    ...

mask = (s1 > 0) & (s2 > 0)  # instead of: (s1 > 0) and (s2 > 0)
if mask.any():           # reduction instead of truth of Series
    ...

Type guard

from polars import Series

def as_bool(x: object) -> bool:
    """Only allow real bools; fail loudly for Series."""
    if isinstance(x, Series):
        raise TypeError("cannot coerce Series to bool; use .any()/.all()/.is_empty()")
    return bool(x)

Try / catch

try:
    flag = bool(result)  # result is sometimes a Series
except TypeError as e:
    if "truth value of a Series is ambiguous" in str(e):
        flag = result.any() if hasattr(result, "any") else False
    else:
        raise

Prevention

When it happens

Trigger: if s: ...; bool(s); s1 and s2; s1 or s2; `s in [a, b]` (uses __eq__ then truth evaluation of the result); passing a Series to assert; using a Series where a plain bool is required (e.g. ternary condition).

Common situations: Code written for a single scalar value later reused with a Series; wrapping results in assert; `if result:` guards after operations that sometimes return Series. The fix depends on intent: emptiness, all/any, or set membership.

Related errors


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