spring-projects/spring-ai · error · RuntimeException

Not supported expression type: {expressionType}

Error message

Not supported expression type: {expressionType}

What it means

MilvusFilterExpressionConverter.getOperationSymbol maps Filter.ExpressionType values to Milvus boolean-expression operator strings. If doExpression encounters an expression type not covered by the switch (notably NOT is absent from the Milvus switch), it throws this RuntimeException. It means the filter expression uses an operator the Milvus converter cannot translate.

Source

Thrown at vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/milvus/MilvusFilterExpressionConverter.java:67

		Assert.state(exp.right() != null, "expected expression.right to be non null");
		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 -> " && ";
			case OR -> " || ";
			case EQ -> " == ";
			case NE -> " != ";
			case LT -> " < ";
			case LTE -> " <= ";
			case GT -> " > ";
			case GTE -> " >= ";
			case IN -> " in ";
			case NIN -> " not in ";
			default -> throw new RuntimeException("Not supported expression type:" + exp.type());
		};
	}

	@Override
	protected void doStartGroup(Group group, StringBuilder context) {
		context.append("(");
	}

	@Override
	protected void doEndGroup(Group group, StringBuilder context) {
		context.append(")");
	}

	@Override
	protected void doKey(Key key, StringBuilder context) {
		var identifier = key.key();
		context.append(this.metadataFieldName).append("[");
		emitJsonValue(identifier, context);

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Rewrite the filter without NOT — e.g. invert the inner comparison (eq -> ne) instead of wrapping in not().
  2. Use only operators the Milvus converter supports: EQ, NE, LT, LTE, GT, GTE, IN, NIN, AND, OR, and grouping.
  3. Align spring-ai core and milvus-store versions so new expression types are handled.
  4. If forking, add the missing case (e.g. NOT via !(...) in Milvus boolean syntax) to getOperationSymbol.

Example fix

// before
new FilterExpressionBuilder().not(new FilterExpressionBuilder().eq("type", "x")).build();
// after
new FilterExpressionBuilder().ne("type", "x").build();
Defensive patterns

Strategy: validation

Validate before calling

private static final Set<Filter.ExpressionType> MILVUS_SUPPORTED =
    Set.of(EQ, NE, LT, LTE, GT, GTE, IN, NIN, AND, OR);
void assertMilvusSafe(Filter.Expression e) {
    if (e.type() == Filter.ExpressionType.NOT)
        throw new IllegalArgumentException("NOT unsupported on Milvus; invert condition");
}

Try / catch

try {
    vectorStore.similaritySearch(request);
} catch (RuntimeException e) {
    if (String.valueOf(e.getMessage()).contains("Not supported expression type")) {
        // retry without the filter or with a rewritten expression
    }
}

Prevention

When it happens

Trigger: Calling MilvusVectorStore.similaritySearch with a SearchRequest whose FilterExpression uses an unsupported type (e.g. FilterExpressionType.NOT, since the Milvus switch has no NOT case), or a new expression type introduced in a newer spring-ai core.

Common situations: Portable filter code shared across vector stores that uses NOT on Milvus; upgrading spring-ai core ahead of the Milvus store module; programmatic filter builders emitting unhandled operators.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/a74c1829897031c4. Report an issue: GitHub.