pandas-dev/pandas · error · ValueError

keyword error in function call

Error message

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

What it means

Raised in the non-FuncNode branch of visit_Call when iterating node.keywords and encountering an element that is not an ast.keyword node. Under normal Python AST grammar every element of Call.keywords is an ast.keyword, so this ValueError is a defensive guard against malformed/constructed AST or an unexpected AST shape produced by the tokenizer/backtick-quoted-string rewriting. It surfaces as a generic 'keyword error in function call' message.

Solutions

  1. Avoid keyword arguments to user functions inside eval/query; pass them positionally.
  2. If you need keyword semantics, call the function in plain Python and pass the result via a @-local variable: r = f(a=1, b=2); df.query('@r > 0').
  3. Simplify the expression: remove backtick-quoted names from keyword positions and use plain identifiers.
  4. If the AST is hand-built, ensure every entry in Call.keywords is an instance of ast.keyword.

Example fix

// before
df.query("@my_fn(`weird name`=1)")

// after
val = my_fn(**{"weird name": 1})
df.query("@val > 0")
Defensive patterns

Strategy: validation

Validate before calling

import ast

def call_keywords_well_formed(expr: str) -> bool:
    tree = ast.parse(expr, mode='eval')
    for node in ast.walk(tree):
        if isinstance(node, ast.Call):
            if any(not isinstance(k, ast.keyword) for k in node.keywords):
                return False
    return True

assert call_keywords_well_formed(query_str)

Type guard

import ast

def keywords_valid(call: ast.Call) -> bool:
    return all(isinstance(k, ast.keyword) for k in call.keywords)

Try / catch

try:
    df.query(query_str)
except ValueError as e:
    if 'keyword error in function call' in str(e):
        # rebuild without keywords, pass args positionally via @-local
        result = fn(*positional_values)
        df.query('@result > 0')
    else:
        raise

Prevention

When it happens

Trigger: Calling a scope-resolved (non-math) function inside eval/query with keyword arguments where one keyword element is not a proper ast.keyword — e.g. df.query('@f(a=1, b=2)') where the AST has been rewritten by backtick/column-name preprocessing into something non-standard. Practically rare; usually indicates a parser edge case or a hand-built expression tree.

Common situations: Backtick-quoted column names used as keyword argument names inside a call to a user function. Mixed positional/keyword calls to user-defined callables in @-local scope. Version regressions in the tokenizer that misclassify keyword tokens.

Related errors


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

Appendix: 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 3b7651241d)