{"record":{"id":"7549133e2488e157","repo":"spring-projects/spring-ai","slug":"expression-of-type-s-requires-a-left-operand","errorCode":null,"errorMessage":"Expression of type %s requires a left operand","messagePattern":"Expression of type (.+?) requires a left operand","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SimpleVectorStoreFilterExpressionEvaluator.java","lineNumber":126,"sourceCode":"\t\t\t\tList<?> list = asList(filterValue(right(expression)), expression);\n\t\t\t\tyield list.stream().anyMatch(item -> compare(metaVal, item) == 0);\n\t\t\t}\n\t\t\tcase NIN -> {\n\t\t\t\tObject metaVal = metadataValue(left(expression), metadata);\n\t\t\t\tList<?> list = asList(filterValue(right(expression)), expression);\n\t\t\t\tyield list.stream().noneMatch(item -> compare(metaVal, item) == 0);\n\t\t\t}\n\t\t\t// Unary operators: only the left operand (the key) is used.\n\t\t\t// A non-null right operand is silently ignored here.\n\t\t\tcase ISNULL -> metadataValue(left(expression), metadata) == null;\n\t\t\tcase ISNOTNULL -> metadataValue(left(expression), metadata) != null;\n\t\t};\n\t}\n\n\tprivate Filter.Operand left(Filter.Expression expression) {\n\t\tFilter.Operand left = expression.left();\n\t\tif (left == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Expression of type %s requires a left operand\".formatted(expression.type()));\n\t\t}\n\t\treturn left;\n\t}\n\n\tprivate Filter.Operand right(Filter.Expression expression) {\n\t\tFilter.Operand right = expression.right();\n\t\tif (right == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Expression of type %s requires a right operand\".formatted(expression.type()));\n\t\t}\n\t\treturn right;\n\t}\n\n\t/**\n\t * Extracts the metadata value for the given {@link Filter.Key} operand. Outer quotes\n\t * ({@code \"...\"} or {@code '...'}) are stripped from the key name to match the format\n\t * used by {@link FilterExpressionBuilder} and the text parser.","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SimpleVectorStoreFilterExpressionEvaluator.java#L108-L144","documentation":"SimpleVectorStoreFilterExpressionEvaluator's left() helper unwraps the left operand of a Filter.Expression. If the expression has no left operand, it throws IllegalArgumentException naming the expression type. This guards internal invariant violations when evaluating filter expressions like EQ, GT, AND, etc.","triggerScenarios":"Calling SimpleVectorStore.search or similaritySearch with a SearchRequest whose filter Expression was programmatically constructed (Filter.Expression) with a null left operand, e.g. new Filter.Expression(EQ, null, new Filter.Value(...)).","commonSituations":"Hand-building Filter.Expression trees instead of using FilterExpressionTextParser or FilterExpressionBuilder; NPE-prone custom DSL code; a Filter.Expression mutated or deserialized incorrectly.","solutions":["Build filter expressions with Filter.ExpressionBuilder or parse a string with FilterExpressionTextParser instead of constructing Filter.Expression manually.","Ensure the left operand of every binary expression is a valid Filter.Key or nested Filter.Expression.","Validate the expression tree before passing it to the vector store search."],"exampleFix":"// before\nnew Filter.Expression(Filter.ExpressionType.EQ, null, new Filter.Value(\"green\"));\n// after\nnew Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key(\"color\"), new Filter.Value(\"green\"));","handlingStrategy":"validation","validationCode":"boolean isWellFormed(Filter.Expression e) {\n    if (e == null) return false;\n    if (e.left() == null || e.right() == null) return false;\n    return true;\n}\n// call before passing expression to SearchRequest","typeGuard":"boolean hasLeftOperand(Filter.Expression e) { return e != null && e.left() != null; }","tryCatchPattern":"try {\n    vectorStore.similaritySearch(request);\n} catch (IllegalArgumentException ex) {\n    if (ex.getMessage().contains(\"requires a left operand\")) {\n        // rebuild expression via FilterExpressionTextParser\n    } else throw ex;\n}","preventionTips":["Prefer FilterExpressionTextParser over manual Filter.Expression construction","Never pass null operands when building Filter.Expression"],"tags":["vector-store","filter-expression","missing-operand","spring-ai"],"backgroundTag":"missing-required-argument","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}