{"record":{"id":"480ea8f36ac1bca4","repo":"spring-projects/spring-ai","slug":"filter-in-right-expression-should-be-of-filter-val","errorCode":null,"errorMessage":"Filter IN right expression should be of Filter.Value type but was ","messagePattern":"Filter IN right expression should be of Filter\\.Value type but was ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterHelper.java","lineNumber":184,"sourceCode":"\t\t\t\t// foo != \"bar2\" && foo != \"bar3\"\n\t\t\t\t// or equivalent to AND(foo != \"bar1\" AND( foo != \"bar2\" OR(foo !=\n\t\t\t\t// \"bar3\")))\n\t\t\t\tList<Filter.Expression> eqExprs = new ArrayList<>();\n\t\t\t\tfor (Object o : list) {\n\t\t\t\t\teqExprs.add(new Filter.Expression(innerExpressionType, exp.left(), new Filter.Value(o)));\n\t\t\t\t}\n\t\t\t\tcontext.append(expressionConverter.convertExpression(aggregate(outerExpressionType, eqExprs)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// 1. foo IN [\"bar\"] is equivalent to foo == \"BAR\"\n\t\t\t\t// 2. foo NIN [\"bar\"] is equivalent to foo != \"BAR\"\n\t\t\t\tcontext.append(expressionConverter\n\t\t\t\t\t.convertExpression(new Filter.Expression(innerExpressionType, exp.left(), exp.right())));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tAssert.state(exp.right() != null, \"Filter IN right expression was null\");\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Filter IN right expression should be of Filter.Value type but was \" + exp.right().getClass());\n\t\t}\n\t}\n\n\t/**\n\t * Recursively aggregates a list of expression into a binary tree with 'aggregateType'\n\t * join nodes.\n\t * @param aggregateType type all tree splits.\n\t * @param expressions list of expressions to aggregate.\n\t * @return Returns a binary tree expression.\n\t */\n\tprivate static Filter.Expression aggregate(Filter.ExpressionType aggregateType,\n\t\t\tList<Filter.Expression> expressions) {\n\n\t\tif (expressions.size() == 1) {\n\t\t\treturn expressions.get(0);\n\t\t}\n\t\treturn new Filter.Expression(aggregateType, expressions.get(0),","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterHelper.java#L166-L202","documentation":"expandInNinExpressions expands IN/NIN expressions into OR/AND trees of EQ/NE comparisons so providers without native IN can evaluate them. The right side of an IN must be a Filter.Value containing the collection; if exp.right() is non-null but not a Filter.Value, it throws IllegalStateException(\"Filter IN right expression should be of Filter.Value type but was \" + class).","triggerScenarios":"Building a Filter.Expression with type IN or NIN whose right() is another Filter.Expression or plain operand (e.g. new Filter.Expression(IN, key, someExpression)), then converting via a converter that calls expandIn/expandNin.","commonSituations":"Hand-constructed filter trees where the author put a nested expression instead of a Value list on the right of IN; code generators producing wrong operand types.","solutions":["Set the right side of IN/NIN to a Filter.Value holding the collection, e.g. new Filter.Value(List.of(\"red\",\"green\"))","Add a validation step on your filter tree before conversion: right() of IN/NIN must be Filter.Value","Catch IllegalStateException from the converter and report the misbuilt expression"],"exampleFix":"// before\nnew Filter.Expression(ExpressionType.IN, new Filter.Key(\"color\"), new Filter.Key(\"colors\"));\n// after\nnew Filter.Expression(ExpressionType.IN, new Filter.Key(\"color\"), new Filter.Value(List.of(\"red\", \"green\")));","handlingStrategy":"validation","validationCode":"Assert.isTrue(exp.type() != ExpressionType.IN && exp.type() != ExpressionType.NIN || exp.right() instanceof Filter.Value, \"IN/NIN right side must be Filter.Value\");","typeGuard":"boolean hasValueRight(Filter.Expression e) { return e.right() instanceof Filter.Value; }","tryCatchPattern":"try { converted = converter.convertExpression(exp); }\ncatch (IllegalStateException e) { throw new IllegalArgumentException(\"Malformed IN expression: \" + e.getMessage(), e); }","preventionTips":["Always put List values in Filter.Value for IN/NIN","Validate filter trees before conversion"],"tags":["filter-expression","in-operator","vector-store"],"backgroundTag":"invalid-argument-value","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}