spring-projects/spring-ai · error · IllegalStateException
The 'NIN' operator should have been transformed into chain…
Error message
The 'NIN' operator should have been transformed into chain of AND/NEQ expressions.
What it means
Analogous to IN, the NIN operator has no native Weaviate equivalent and must be transformed into a chain of AND/NEQ expressions before the operator symbol is generated. getOperationSymbol throws this IllegalStateException if it still sees an NIN expression, meaning the pre-transformation was skipped.
Solutions
- Use the full standard conversion path so NIN is expanded into AND of NEQ automatically.
- Manually rewrite the filter as AND of NEQ expressions: (a!=1 AND a!=2).
- Fix subclass overrides of doExpression to preserve the NIN-to-AND/NEQ expansion.
Example fix
// before
var expr = new Filter.Expression(NIN, new Key("status"), new Value(List.of("draft")));
// after
var ne1 = new Filter.Expression(NE, new Key("status"), new Value("draft"));
var ne2 = new Filter.Expression(NE, new Key("status"), new Value("archived"));
var expr = new Filter.Expression(AND, ne1, ne2); Defensive patterns
Strategy: type-guard
Validate before calling
if (expr.type() == Filter.ExpressionType.NIN) { throw new IllegalArgumentException("Use AND/NEQ chains or the standard conversion path for NIN"); } 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("'NIN' operator")) { /* expand NIN into AND/NEQ yourself */ } else throw e; } Prevention
- Route NIN through the standard converter path which expands it
- Never invoke getOperationSymbol on raw NIN expressions
- Keep doExpression overrides expansion-preserving
When it happens
Trigger: Converting a Filter.Expression of type NIN without letting the converter's doExpression preprocessing expand it, e.g. direct calls to getOperationSymbol or subclass overrides that bypass the expansion.
Common situations: Direct/converter-level usage bypassing the standard pipeline; custom subclasses overriding doExpression; hand-constructed expression trees passed to a partial conversion step.
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 'IN' operator should have been transformed into chain…
- Expression type not supported for numeric fields
- Expression type not yet implemented. Patches welcome.
- Not allowed filter identifier name: . Consider adding it to…
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/67c82e6ea0c70837.
Report an issue: GitHub.
Appendix: source
Thrown at vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/weaviate/WeaviateFilterExpressionConverter.java:120
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)