pola-rs/polars · error · TypeError
`map` with `returns_scalar=False` must return a Series; foun
Error message
`map` with `returns_scalar=False` must return a Series; found {qualified_type_name(rv)!r}.
If `returns_scalar` is set to `True`, a returned value can be a scalar value. What it means
In F.map_batches (the multi-expression variant), the user-supplied function must return a pl.Series; a numpy ndarray return is auto-wrapped into one. Any other return value — list, tuple, None, a bare Python scalar, or a DataFrame — raises TypeError naming the actual type when returns_scalar=False (the default). Scalars are accepted only with returns_scalar=True.
Source
Thrown at py-polars/src/polars/functions/lazy.py:1113
try:
rv = self.function(slp, *args, **kwargs)
except TypeError as e:
if "unexpected keyword argument 'return_dtype'" in e.args[0]:
kwargs.pop("return_dtype")
rv = self.function(slp, *args, **kwargs)
else:
raise
if _check_for_numpy(rv) and isinstance(rv, np.ndarray):
rv = pl.Series(rv, dtype=return_dtype)
if isinstance(rv, pl.Series):
return rv._s
elif self.returns_scalar:
return pl.Series([rv], dtype=return_dtype)._s
else:
msg = f"`map` with `returns_scalar=False` must return a Series; found {qualified_type_name(rv)!r}.\n\nIf `returns_scalar` is set to `True`, a returned value can be a scalar value."
raise TypeError(msg)
def map_batches(
exprs: Sequence[str | Expr],
function: Callable[[Sequence[Series]], Series | Any],
return_dtype: PolarsDataType | pl.DataTypeExpr | None = None,
*,
is_elementwise: bool = False,
returns_scalar: bool = False,
) -> Expr:
"""
Map a custom function over multiple columns/expressions.
Produces a single Series result.
.. warning::
This method is much slower than the native expressions API.
Only use it if you cannot implement your logic otherwise.View on GitHub (pinned to df599052da)
Solutions
- Return Series directly via vectorized ops: lambda ss: ss[0] * ss[1]
- Wrap list results explicitly: return pl.Series(result)
- If the function yields one value per call, pass returns_scalar=True
- Fix None branches to return a typed empty Series, e.g. pl.Series(dtype=pl.Float64)
Example fix
# before pl.map_batches(['a', 'b'], lambda ss: [v0 * v1 for v0, v1 in zip(ss[0], ss[1])]) # list -> TypeError # after pl.map_batches(['a', 'b'], lambda ss: ss[0] * ss[1]) # scalar result: pl.map_batches(['a'], lambda ss: ss[0].mean(), returns_scalar=True)
Defensive patterns
Strategy: validation
Validate before calling
def as_series(rv):
if isinstance(rv, pl.Series):
return rv
if _check_for_numpy(rv) and isinstance(rv, np.ndarray):
return pl.Series(rv)
if not isinstance(rv, pl.Series):
return pl.Series(rv) # wrap list-like; scalars need returns_scalar=True
return rv
expr = pl.map_batches(['a', 'b'], lambda ss: as_series(my_fn(*ss))) Type guard
def returns_series(fn, sample: list[pl.Series]) -> bool:
return isinstance(fn(*sample), pl.Series) Prevention
- Unit-test the mapping function against a small frame before wiring it into map_batches
- Prefer vectorized Series operations inside the callback; return the result of Series arithmetic
- Remember numpy arrays are auto-converted but plain lists and scalars are not; set returns_scalar=True for scalars
When it happens
Trigger: A callback returning [x.mean() for x in ...] (list comprehension); returning float/int with default returns_scalar=False; an early-exit branch returning None; returning a DataFrame column object instead of a Series.
Common situations: Porting pandas .apply code that returns lists; wrapping numpy-style functions (arrays are fine, scalars are not); forgetting returns_scalar=True for per-batch aggregates.
Related errors
- cannot select columns using key of type {qualified_type_name
- expected {df.width} values when selecting columns by boolean
- index {key} is out of bounds for DataFrame of height {num_ro
- cannot select rows using key of type {qualified_type_name(ke
- cannot treat Series of type {s.dtype} as indices
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/35c6275863dc5535.
Report an issue: GitHub.