{"record":{"id":"1455bf6e5c763777","repo":"pandas-dev/pandas","slug":"keyword-error-in-function-call-node-func-id","errorCode":null,"errorMessage":"keyword error in function call '{node.func.id}'","messagePattern":"keyword error in function call '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/computation/expr.py","lineNumber":708,"sourceCode":"\n        if isinstance(res, FuncNode):\n            new_args = [self.visit(arg) for arg in node.args]\n\n            if node.keywords:\n                raise TypeError(\n                    f'Function \"{res.name}\" does not support keyword arguments'\n                )\n\n            return res(*new_args)\n\n        else:\n            new_args = [self.visit(arg)(self.env) for arg in node.args]\n\n            for key in node.keywords:\n                if not isinstance(key, ast.keyword):\n                    # error: Item \"Attribute\" of \"Attribute | Name\" has no\n                    # attribute \"id\"\n                    raise ValueError(\n                        f\"keyword error in function call '{node.func.id}'\"  # type: ignore[union-attr]\n                    )\n\n                if key.arg:\n                    kwargs[key.arg] = self.visit(key.value)(self.env)\n\n            name = self.env.add_tmp(res(*new_args, **kwargs))\n            return self.term_type(name=name, env=self.env)\n\n    def translate_In(self, op):\n        return op\n\n    def visit_Compare(self, node, **kwargs):\n        ops = node.ops\n        comps = node.comparators\n\n        # base case: we have something like a CMP b\n        if len(comps) == 1:","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/computation/expr.py#L690-L726","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Avoid keyword arguments to user functions inside eval/query; pass them positionally.","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').","Simplify the expression: remove backtick-quoted names from keyword positions and use plain identifiers.","If the AST is hand-built, ensure every entry in Call.keywords is an instance of ast.keyword."],"exampleFix":"// before\ndf.query(\"@my_fn(`weird name`=1)\")\n\n// after\nval = my_fn(**{\"weird name\": 1})\ndf.query(\"@val > 0\")","handlingStrategy":"validation","validationCode":"import ast\n\ndef call_keywords_well_formed(expr: str) -> bool:\n    tree = ast.parse(expr, mode='eval')\n    for node in ast.walk(tree):\n        if isinstance(node, ast.Call):\n            if any(not isinstance(k, ast.keyword) for k in node.keywords):\n                return False\n    return True\n\nassert call_keywords_well_formed(query_str)","typeGuard":"import ast\n\ndef keywords_valid(call: ast.Call) -> bool:\n    return all(isinstance(k, ast.keyword) for k in call.keywords)","tryCatchPattern":"try:\n    df.query(query_str)\nexcept ValueError as e:\n    if 'keyword error in function call' in str(e):\n        # rebuild without keywords, pass args positionally via @-local\n        result = fn(*positional_values)\n        df.query('@result > 0')\n    else:\n        raise","preventionTips":["Avoid backtick-quoted names in keyword positions inside eval/query.","Prefer calling user functions in plain Python and passing results via @-locals.","Validate the AST shape of generated query strings in tests."],"tags":["pandas","eval","query","keyword-arguments","ast","defensive"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}