pandas-dev/pandas · error · ValueError

Invalid function call {node.func.id}

Error message

Invalid function call {node.func.id}

What it means

In visit_Call, after attempting to resolve node.func (via visit or FuncNode lookup), if the result res is None the function name resolved to nothing callable and pandas raises ValueError naming node.func.id. This typically means a name in the expression resolved to None in scope, or a FuncNode lookup path returned None.

Source

Thrown at pandas/core/computation/expr.py:685

    def visit_Call(self, node, side=None, **kwargs):
        if isinstance(node.func, ast.Attribute) and node.func.attr != "__call__":
            res = self.visit_Attribute(node.func)
        elif not isinstance(node.func, ast.Name):
            raise TypeError("Only named functions are supported")
        else:
            try:
                res = self.visit(node.func)
            except UndefinedVariableError:
                # Check if this is a supported function name
                try:
                    res = FuncNode(node.func.id)
                except ValueError:
                    # Raise original error
                    raise

        if res is None:
            # error: "expr" has no attribute "id"
            raise ValueError(
                f"Invalid function call {node.func.id}"  # type: ignore[union-attr]
            )
        if hasattr(res, "value"):
            res = res.value

        if isinstance(res, FuncNode):
            new_args = [self.visit(arg) for arg in node.args]

            if node.keywords:
                raise TypeError(
                    f'Function "{res.name}" does not support keyword arguments'
                )

            return res(*new_args)

        else:
            new_args = [self.visit(arg)(self.env) for arg in node.args]

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Verify the function name is spelled correctly and is actually callable in scope.
  2. Rename the colliding variable/column so the function name resolves to the callable.
  3. Register the function in local_dict/global_dict explicitly.

Example fix

// before
foo = None
df.eval('foo(a)')
// after
import math as foo_math
pd.eval('foo_math.sqrt(a)', local_dict={'foo_math': foo_math})
Defensive patterns

Strategy: validation

Validate before calling

def validate_callable_in_scope(name: str, local_dict, global_dict) -> None:
    obj = (local_dict or {}).get(name, (global_dict or {}).get(name))
    if obj is None:
        raise ValueError(f'{name!r} resolves to None; cannot be called in eval')

# before evaluating 'foo(a)':
validate_callable_in_scope('foo', local_dict, global_dict)

Type guard

def name_resolves_to_callable(name: str, local_dict, global_dict) -> bool:
    obj = (local_dict or {}).get(name, (global_dict or {}).get(name))
    return callable(obj)

Try / catch

try:
    df.eval(expr)
except ValueError as e:
    if 'Invalid function call' in str(e):
        # ensure the function is registered before retrying
        pd.eval(expr, local_dict={**locals()})
    raise

Prevention

When it happens

Trigger: df.eval('foo(a)') where 'foo' is a name in scope whose value is None, or a callable name that fails resolution and yields None through a custom resolver.

Common situations: Variable shadowing where a column or local named like the intended function holds None. Resolvers that return None for unknown names instead of raising UndefinedVariableError.

Related errors


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