{"record":{"id":"df51ec19414f386d","repo":"microsoft/semantic-kernel","slug":"left-side-must-be-x-field","errorCode":null,"errorMessage":"Left side must be x.FIELD.","messagePattern":"Left side must be x\\.FIELD\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/_search_shared.py","lineNumber":30,"sourceCode":"\n\nclass SearchLambdaVisitor(ast.NodeVisitor):\n    \"\"\"Visitor to parse a lambda function for Brave and Google Search filters.\"\"\"\n\n    def __init__(self, valid_parameters: list[str]):\n        \"\"\"Initialize the visitor with a list of valid parameters.\"\"\"\n        self.filters: list[dict[str, str]] = []\n        self.valid_parameters = valid_parameters\n\n    @override\n    def visit_Lambda(self, node):\n        self.visit(node.body)\n\n    @override\n    def visit_Compare(self, node):\n        # Only support x.FIELD == VALUE\n        if not (isinstance(node.left, ast.Attribute) and isinstance(node.left.value, ast.Name)):\n            raise NotImplementedError(\"Left side must be x.FIELD.\")\n        field = node.left.attr\n        if not (len(node.ops) == 1 and isinstance(node.ops[0], ast.Eq)):\n            raise NotImplementedError(\"Only == comparisons are supported.\")\n        right = node.comparators[0]\n        if isinstance(right, ast.Constant):\n            if right.value is None:\n                raise NotImplementedError(\"None values are not supported.\")\n            value = str(right.value)\n        else:\n            raise NotImplementedError(\"Only constant values are supported on the right side.\")\n        if field not in self.valid_parameters:\n            raise ValueError(f\"Field '{field}' is not supported.\")\n        self.filters.append({field: quote_plus(value)})\n\n    @override\n    def visit_BoolOp(self, node):\n        if not isinstance(node.op, ast.And):\n            raise NotImplementedError(\"Only 'and' of == comparisons is supported.\")","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/_search_shared.py#L12-L48","documentation":"SearchLambdaVisitor (used by Brave/Google search filter lambdas) only accepts comparisons of the exact shape `x.FIELD == VALUE`. The left operand must be an ast.Attribute access on an ast.Name (the lambda parameter). Anything else on the left raises NotImplementedError.","triggerScenarios":"Supplying a filter lambda whose left side is not `x.FIELD`: a reversed comparison (`lambda x: VALUE == x.field`), a call expression (`func(x.field) == ...`), a subscript, or a bare comparison without an attribute.","commonSituations":"Writing the value first; wrapping the field in a function; using a comparison the visitor does not model.","solutions":["Rewrite the filter so the left side is the parameter attribute: `lambda x: x.field == VALUE`.","Move any transformation into the constant value, not the field side."],"exampleFix":"// before\nlambda x: \"news\" == x.category   # value on the left\nlambda x: str(x.year) == \"2024\"  # call on the left\n\n// after\nlambda x: x.category == \"news\"\nlambda x: x.year == 2024","handlingStrategy":"validation","validationCode":"import ast\nnode = ast.parse(filter_src, mode='eval').body  # the lambda\ndef check_left(n):\n    if isinstance(n, ast.Compare):\n        assert isinstance(n.left, ast.Attribute) and isinstance(n.left.value, ast.Name), 'left must be x.FIELD'\n    if isinstance(n, ast.BoolOp):\n        for v in n.values: check_left(v)\ncheck_left(node.body)","typeGuard":"def is_x_field_compare(node: ast.Compare) -> bool:\n    return isinstance(node.left, ast.Attribute) and isinstance(node.left.value, ast.Name)","tryCatchPattern":null,"preventionTips":["Write filter lambdas as `lambda x: x.FIELD == VALUE` only","Keep the field on the left, value on the right","Validate filter syntax against the documented DSL before runtime"],"tags":["search","filters","lambda","ast"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}