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

  1. Use the full standard conversion path so NIN is expanded into AND of NEQ automatically.
  2. Manually rewrite the filter as AND of NEQ expressions: (a!=1 AND a!=2).
  3. 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

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


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)