spring-projects/spring-ai · error · IllegalStateException
Filter IN right expression should be of Filter.Value type…
Error message
Filter IN right expression should be of Filter.Value type but was
What it means
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).
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
Example fix
// before
new Filter.Expression(ExpressionType.IN, new Filter.Key("color"), new Filter.Key("colors"));
// after
new Filter.Expression(ExpressionType.IN, new Filter.Key("color"), new Filter.Value(List.of("red", "green"))); Defensive patterns
Strategy: validation
Validate before calling
Assert.isTrue(exp.type() != ExpressionType.IN && exp.type() != ExpressionType.NIN || exp.right() instanceof Filter.Value, "IN/NIN right side must be Filter.Value");
Type guard
boolean hasValueRight(Filter.Expression e) { return e.right() instanceof Filter.Value; } Try / catch
try { converted = converter.convertExpression(exp); }
catch (IllegalStateException e) { throw new IllegalArgumentException("Malformed IN expression: " + e.getMessage(), e); } Prevention
- Always put List values in Filter.Value for IN/NIN
- Validate filter trees before conversion
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Expected a List value for
- The 'IN' operator should have been transformed into chain…
- Cannot compare values of incompatible types
- Cannot compare values of incompatible types
- Cannot compare values of types
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/480ea8f36ac1bca4.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterHelper.java:184
// foo != "bar2" && foo != "bar3"
// or equivalent to AND(foo != "bar1" AND( foo != "bar2" OR(foo !=
// "bar3")))
List<Filter.Expression> eqExprs = new ArrayList<>();
for (Object o : list) {
eqExprs.add(new Filter.Expression(innerExpressionType, exp.left(), new Filter.Value(o)));
}
context.append(expressionConverter.convertExpression(aggregate(outerExpressionType, eqExprs)));
}
else {
// 1. foo IN ["bar"] is equivalent to foo == "BAR"
// 2. foo NIN ["bar"] is equivalent to foo != "BAR"
context.append(expressionConverter
.convertExpression(new Filter.Expression(innerExpressionType, exp.left(), exp.right())));
}
}
else {
Assert.state(exp.right() != null, "Filter IN right expression was null");
throw new IllegalStateException(
"Filter IN right expression should be of Filter.Value type but was " + exp.right().getClass());
}
}
/**
* Recursively aggregates a list of expression into a binary tree with 'aggregateType'
* join nodes.
* @param aggregateType type all tree splits.
* @param expressions list of expressions to aggregate.
* @return Returns a binary tree expression.
*/
private static Filter.Expression aggregate(Filter.ExpressionType aggregateType,
List<Filter.Expression> expressions) {
if (expressions.size() == 1) {
return expressions.get(0);
}
return new Filter.Expression(aggregateType, expressions.get(0),View on GitHub (pinned to 98a7beda4f)