spring-projects/spring-ai · error · IllegalStateException
The 'IN' operator should have been transformed into chain…
Error message
The 'IN' operator should have been transformed into chain of OR/EQ expressions.
What it means
The Weaviate converter cannot emit IN directly: before conversion, IN expressions are (or must be) transformed into a chain of OR/EQ expressions, because Weaviate's GraphQL where filter has no native IN operator. Encountering IN in getOperationSymbol means the pre-transformation step didn't run, so an IllegalStateException is thrown.
Solutions
- Let the standard conversion path (convert/doExpression) handle the expression — it expands IN into OR of EQ automatically.
- Manually rewrite your filter as OR of EQ expressions: (a==1 OR a==2 OR a==3).
- If you override doExpression in a subclass, ensure you still expand IN/NIN before delegating to getOperationSymbol.
Example fix
// before
var expr = new Filter.Expression(IN, new Key("color"), new Value(List.of("red", "blue"))); // passed straight to operand conversion
// after
var or1 = new Filter.Expression(EQ, new Key("color"), new Value("red"));
var or2 = new Filter.Expression(EQ, new Key("color"), new Value("blue"));
var expr = new Filter.Expression(OR, or1, or2); Defensive patterns
Strategy: type-guard
Validate before calling
if (expr.type() == Filter.ExpressionType.IN) { throw new IllegalArgumentException("Use OR/EQ chains or the standard conversion path for IN"); } Type guard
static boolean requiresExpansion(Filter.Expression e) { return e.type() == Filter.ExpressionType.IN || e.type() == Filter.ExpressionType.NIN; } Try / catch
try { String q = converter.convertExpression(expr); } catch (IllegalStateException e) { if (e.getMessage().contains("'IN' operator")) { /* expand IN into OR/EQ yourself */ } else throw e; } Prevention
- Always convert through the full converter pipeline, never call getOperationSymbol directly
- Express set membership with IN and let the converter expand it
- Avoid subclass overrides that skip the IN expansion step
When it happens
Trigger: Calling the converter with a Filter.Expression of type IN that bypassed the standard doExpression transformation, e.g. invoking getOperationSymbol directly or a code path that skips the IN-expansion preprocessing.
Common situations: Custom code calling the converter internals directly; a subclass overriding doExpression and removing the IN expansion; version mismatch where the transformation was expected upstream.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Not supported expression type:
- The 'NIN' operator should have been transformed into chain…
- Expected a List value for
- Expression type not supported for numeric fields
- Expression type not yet implemented. Patches welcome.
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/824809862cd158be.
Report an issue: GitHub.
Appendix: source
Thrown at vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/weaviate/WeaviateFilterExpressionConverter.java:118
}
else {
this.convertOperand(exp.left(), context);
context.append(getOperationSymbol(exp));
this.convertOperand(exp.right(), context);
}
}
private String getOperationSymbol(Expression exp) {
return switch (exp.type()) {
case AND -> "operator:And \n";
case OR -> "operator:Or \n";
case EQ -> "operator:Equal \n";
case NE -> "operator:NotEqual \n";
case LT -> "operator:LessThan \n";
case LTE -> "operator:LessThanEqual \n";
case GT -> "operator:GreaterThan \n";
case GTE -> "operator:GreaterThanEqual \n";
case IN -> throw new IllegalStateException(
"The 'IN' operator should have been transformed into chain of OR/EQ expressions.");
case NIN -> throw new IllegalStateException(
"The 'NIN' operator should have been transformed into chain of AND/NEQ expressions.");
default -> throw new UnsupportedOperationException("Not supported expression type:" + exp.type());
};
}
@Override
protected void doKey(Key key, StringBuilder context) {
var identifier = (hasOuterQuotes(key.key())) ? removeOuterQuotes(key.key()) : key.key();
context.append("path:[");
emitJsonValue(withMetaPrefix(identifier), context);
context.append("] \n");
}
public String withMetaPrefix(String identifier) {
if (SYSTEM_IDENTIFIERS.contains(identifier)) {
return identifier;View on GitHub (pinned to 98a7beda4f)