pola-rs/polars · error · NotImplementedError

could not find `apply_ufunc_

Error message

could not find `apply_ufunc_{numpy_char_code_to_dtype(dtype_char)}`

What it means

After determining the minimum numpy dtype char for a ufunc, polars looks up a compiled helper named apply_ufunc_<dtype> in its Rust core to run the ufunc with that dtype. If no such helper exists for the resolved dtype char, it raises NotImplementedError.

Solutions

  1. Cast inputs to a supported dtype first, e.g. srs.cast(pl.Float64)
  2. Avoid dtypes that promote to unsupported result types (use float64/int64/float32)
  3. Perform the ufunc in numpy and rebuild the Series from the result

Example fix

// before
np.exp(srs.cast(pl.Float16))
// after
np.exp(srs.cast(pl.Float64))
Defensive patterns

Strategy: type-guard

Validate before calling

assert srs.dtype in (pl.Int64, pl.Float64, pl.Float32, pl.Int32), f"unsupported ufunc dtype {srs.dtype}"

Type guard

def ufunc_safe_dtype(dt: pl.DataType) -> bool:
    return dt in (pl.Int8, pl.Int16, pl.Int32, pl.Int64, pl.UInt8, pl.UInt16, pl.UInt32, pl.UInt64, pl.Float32, pl.Float64)

Try / catch

try:
    out = np.exp(srs)
except NotImplementedError:
    out = np.exp(srs.cast(pl.Float64))

Prevention

When it happens

Trigger: Calling a numpy ufunc on a Series whose arguments resolve to a dtype char with no registered apply_ufunc_* helper (e.g. exotic result types from np.result_type such as complex/float16 in some builds, or object dtype).

Common situations: Using ufuncs that promote to an unusual dtype (e.g. np.float16 result, complex128) not covered by polars' ufunc dispatch table.

Related errors


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

Appendix: source

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

                ufunc_input, ufunc_output = ufunc.signature.split("->")
                if ufunc_output == "()":
                    # If the result a scalar, just let the function do its
                    # thing, no need for any song and dance involving
                    # allocation:
                    return ufunc(*args, dtype=dtype_char, **kwargs)
                else:
                    allocate_output = ufunc_input == ufunc_output
            else:
                allocate_output = True

            f = get_ffi_func("apply_ufunc_<>", numpy_char_code_to_dtype(dtype_char), s)

            if f is None:
                msg = (
                    "could not find "
                    f"`apply_ufunc_{numpy_char_code_to_dtype(dtype_char)}`"
                )
                raise NotImplementedError(msg)

            series = f(
                lambda out: ufunc(*args, out=out, dtype=dtype_char, **kwargs),
                allocate_output,
            )

            result = self._from_pyseries(series)
            if is_generalized_ufunc:
                # In this case we've disallowed passing in missing data, so no
                # further processing is needed.
                return result

            # We're using a regular ufunc, that operates value by value. That
            # means we allowed missing data in the input, so filter it out:
            validity_mask = self.is_not_null() if self.has_nulls() else F.lit(True)
            for arg in inputs:
                if isinstance(arg, Series) and arg.has_nulls():
                    validity_mask &= arg.is_not_null()

View on GitHub (pinned to fe841f959e)