pandas-dev/pandas · error · AttributeError

'{}' is not a valid function for '{type(obj).__name__}' obje

Error message

'{}' is not a valid function for '{type(obj).__name__}' object

What it means

Raised in `_apply_str` when the string `func` is neither an attribute of the target object nor a numpy function available on its `__array__`. pandas tries `getattr(obj, func)` first, then `getattr(np, func)`; if both fail, it raises AttributeError indicating the name is invalid for this object type.

Source

Thrown at pandas/core/apply.py:846

        assert isinstance(func, str)

        if hasattr(obj, func):
            f = getattr(obj, func)
            if callable(f):
                return f(*args, **kwargs)

            # people may aggregate on a non-callable attribute
            # but don't let them think they can pass args to it
            assert len(args) == 0
            assert not any(kwarg == "axis" for kwarg in kwargs)
            return f
        elif hasattr(np, func) and hasattr(obj, "__array__"):
            # in particular exclude Window
            f = getattr(np, func)
            return f(obj, *args, **kwargs)
        else:
            msg = f"'{func}' is not a valid function for '{type(obj).__name__}' object"
            raise AttributeError(msg)


class NDFrameApply(Apply):
    """
    Methods shared by FrameApply and SeriesApply but
    not GroupByApply or ResamplerWindowApply
    """

    obj: DataFrame | Series

    @property
    def index(self) -> Index:
        return self.obj.index

    @property
    def agg_axis(self) -> Index:
        return self.obj._get_agg_axis(self.axis)

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Check that the method exists on the object: `hasattr(df, func)` or `hasattr(np, func)`.
  2. Pass the callable directly instead of a string: `df.apply(np.sqrt)` rather than `df.apply('sqrt')`.
  3. For element-wise numpy functions, use `df.applymap(np.sqrt)` (or `df.map` on newer versions).

Example fix

# before
df.apply('sqrt')
# after
df.apply(np.sqrt)
# or for element-wise
df.map(np.sqrt)
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np

def resolve_func_name(obj, name):
    if hasattr(obj, name) and callable(getattr(obj, name)):
        return name
    if hasattr(np, name) and hasattr(obj, '__array__'):
        return name
    raise AttributeError(f'{name!r} not valid for {type(obj).__name__}')

# usagedf.apply(resolve_func_name(df, candidate))

Type guard

def is_valid_func_name(obj, name) -> bool:
    import numpy as np
    return (hasattr(obj, name) and callable(getattr(obj, name))) or (hasattr(np, name) and hasattr(obj, '__array__'))

Try / catch

try:
    df.apply(name)
except AttributeError as e:
    if 'is not a valid function' in str(e):
        df.apply(getattr(np, name))  # fall back to numpy callable
    else:
        raise

Prevention

When it happens

Trigger: `df.apply('nonexistent_method')`, `series.apply('mean_x')` (typo), or `df.apply('sqrt')` when 'sqrt' is not a DataFrame method (it is a numpy/Series ufunc but not a DataFrame method). Different object types expose different method sets.

Common situations: Typo in a method name; assuming a method exists on DataFrame when it only exists on Series (or vice versa); version upgrades that renamed/removed a method; passing a numpy function name that the object type does not expose.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/647e6bbe6c3ddd2e. Report an issue: GitHub.