pola-rs/polars · error · TypeError

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

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

Polars refuses to convert a multi-element Series to a single boolean, because the result would be ambiguous (does `if s` mean 'non-empty', 'all true', or 'any true'?). Series.__bool__ deliberately raises TypeError with guidance pointing to the intended alternatives. This mirrors pandas' classic 'truth value of a Series is ambiguous' error, which almost always indicates a Python boolean operator was used where an elementwise one was meant.

Solutions

  1. Replace boolean operators with elementwise ones: `s1 and s2` -> `s1 & s2`, `s1 or s2` -> `s1 | s2`, `not s` -> `~s` (wrap compound comparisons in parentheses due to precedence).
  2. If the intent was an emptiness check, use `if not s.is_empty():` or `if len(s) == 0:`.
  3. If the intent was membership, use `s.is_in([y, z])` instead of `s in [y, z]`.
  4. If you expected a scalar, extract it explicitly before the boolean context: `if s.item():` (fails loudly if length != 1), or use `s.any()`, `s.all()`, `s.sum()` as appropriate.

Example fix

// before
if (s > 0) and (s < 10):   # TypeError: the truth value of a Series is ambiguous
    ...

// after
mask = (s > 0) & (s < 10)
if mask.all():
    ...
Defensive patterns

Strategy: type-guard

Validate before calling

assert isinstance(x, pl.Series)
if not isinstance(x, bool):
    # a Series in a boolean context will raise; reduce explicitly first
    x = x.all()  # or x.any() / x.item(), depending on intent

Type guard

def is_scalar_bool(v) -> bool:
    return isinstance(v, bool)  # only bare bools are safe in if/and/or/not

Try / catch

try:
    if s and other:
        ...
except TypeError as e:
    if 'truth value of a Series is ambiguous' in str(e):
        if (s & other).all():
            ...
    else:
        raise

Prevention

When it happens

Trigger: Using a Series in any boolean context: `if s:`, `assert s`, `s1 and s2`, `s1 or s2`, `not s`, `s in [y, z]`, or `bool(s)` — anything that invokes bool() on the Series (e.g. a function expected to return a scalar actually returning a Series, then used in `if`).

Common situations: Translating scalar if-statements from pandas/numpy code; chaining conditions with `and`/`or` instead of `&`/`|`; membership checks written as `x in [s1, s2]`; filtering results expected to be scalars but returned as one-element Series.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/e09a5c6f1bffdf27. Report an issue: GitHub.

Appendix: source

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

        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 fe841f959e)