{"record":{"id":"59a2922bcb870ef1","repo":"spring-projects/spring-ai","slug":"expression-of-type-s-requires-a-left-operand-59a292","errorCode":null,"errorMessage":"Expression of type %s requires a left operand","messagePattern":"Expression of type (.+?) requires a left operand","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-s3-vector-store/src/main/java/org/springframework/ai/vectorstore/s3/S3VectorStoreFilterExpressionEvaluator.java","lineNumber":84,"sourceCode":"\t\t\tcase IN -> {\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().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\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\tprivate @Nullable Object metadataValue(Filter.Operand operand, Map<String, Object> metadata) {\n\t\tif (operand instanceof Filter.Key key) {\n\t\t\tString k = key.key();\n\t\t\tif (k.length() >= 2","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-s3-vector-store/src/main/java/org/springframework/ai/vectorstore/s3/S3VectorStoreFilterExpressionEvaluator.java#L66-L102","documentation":"The evaluator's left() accessor throws IllegalArgumentException when a Filter.Expression has no left operand but the expression type requires one. All operators evaluated by evaluateExpression (AND, OR, NOT, comparisons, IN/NIN, ISNULL/ISNOTNULL) need a left side, so a null left operand is treated as a malformed filter.","triggerScenarios":"Constructing a Filter.Expression with a null left operand — most typically a NOT expression built as new Filter.Expression(NOT, null, child) — then evaluating it via S3VectorStore similaritySearch post-filtering.","commonSituations":"NOT misuse: some filter models put the negated child on the right, but this evaluator reads NOT's child from the left; manual expression construction; migrating filters from stores where NOT carries its operand on the right.","solutions":["For NOT, place the negated expression as the LEFT operand: new Filter.Expression(NOT, groupOrExpression, null).","Use the Filter builder/expr helper to construct expressions so operand placement is correct.","Before evaluation, assert expression.left() != null for every node you build programmatically.","Catch IllegalArgumentException around similaritySearch and inspect the type named in the message."],"exampleFix":"// before\nnew Filter.Expression(Filter.ExpressionType.NOT, null, Filter.group(eq(\"a\", 1)))\n// after\nnew Filter.Expression(Filter.ExpressionType.NOT, Filter.group(eq(\"a\", 1)), null)","handlingStrategy":"validation","validationCode":"static void requireLeft(Filter.Expression e) { if (e.left() == null) throw new IllegalStateException(\"Expression \" + e.type() + \" needs a left operand\"); }","typeGuard":"static boolean hasLeft(Filter.Expression e) { return e.left() != null; }","tryCatchPattern":"try { store.similaritySearch(req); } catch (IllegalArgumentException e) { if (e.getMessage().endsWith(\"requires a left operand\")) { /* fix NOT operand placement */ } }","preventionTips":["Put NOT's negated child on the LEFT operand slot.","Assert non-null operands on every node of hand-built filter trees.","Prefer Filter.expr(...) parsing over manual expression construction."],"tags":["vector-store","filter-expression","null-operand","not-operator","aws-s3-vectors"],"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-14T11:17:12.474Z"}