pandas-dev/pandas · error · ValueError

keyword error in function call '{node.func.id}'

Error message

keyword error in function call '{node.func.id}'

What it means

In the non-FuncNode branch of visit_Call (expr.py:701), each element of node.keywords is expected to be an ast.keyword node. The check at expr.py:705 is defensive: for any syntactically valid Python call, Python's parser always produces ast.keyword entries, so reaching the raise indicates either hand-built/malformed AST or a preparser that injected non-keyword nodes. The message names node.func.id to help locate the offending call.

Source

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

        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]

            for key in node.keywords:
                if not isinstance(key, ast.keyword):
                    # error: Item "Attribute" of "Attribute | Name" has no
                    # attribute "id"
                    raise ValueError(
                        f"keyword error in function call '{node.func.id}'"  # type: ignore[union-attr]
                    )

                if key.arg:
                    kwargs[key.arg] = self.visit(key.value)(self.env)

            name = self.env.add_tmp(res(*new_args, **kwargs))
            return self.term_type(name=name, env=self.env)

    def translate_In(self, op):
        return op

    def visit_Compare(self, node, **kwargs):
        ops = node.ops
        comps = node.comparators

        # base case: we have something like a CMP b
        if len(comps) == 1:

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Avoid constructing ast.Call nodes manually; pass string expressions instead.
  2. If you must build AST, ensure every keywords entry is an ast.keyword.
  3. Simplify the expression to a plain positional call and report the edge case upstream.

Example fix

// before
# hand-built ast.Call with a non-keyword entry in node.keywords
// after
# pass a plain string expression so Python's parser builds correct AST:
df.eval('foo(a, b=1)')
Defensive patterns

Strategy: validation

Validate before calling

import ast

def validate_keywords_wellformed(expr: str) -> None:
    for node in ast.walk(ast.parse(expr, mode='eval')):
        if isinstance(node, ast.Call):
            for kw in node.keywords:
                if not isinstance(kw, ast.keyword):
                    raise ValueError(
                        f'malformed keyword node in call {ast.dump(node)}'
                    )

# this guards only hand-built AST; for string input Python's parser guarantees this
validate_keywords_wellformed(expr)

Type guard

import ast

def call_keywords_wellformed(expr: str) -> bool:
    return all(
        isinstance(kw, ast.keyword)
        for n in ast.walk(ast.parse(expr, mode='eval'))
        if isinstance(n, ast.Call) for kw in n.keywords
    )

Try / catch

try:
    df.eval(expr)
except ValueError as e:
    if 'keyword error in function call' in str(e):
        # simplify the call to positional args and retry
        df.eval('foo(a, b)')
    raise

Prevention

When it happens

Trigger: Effectively unreachable from normal string input. Surfaces only with manually constructed ast.Call nodes whose keywords list contains non-ast.keyword items, or with a buggy preparser that mangles the keyword list.

Common situations: Internal tooling or third-party libraries that build AST and feed it to pandas. Debugging custom visitors. Note: the surrounding code also references an uninitialized 'kwargs' dict (expr.py:713), so this region is best treated as internal/defensive.

Related errors


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