spring-projects/spring-ai · error · IllegalStateException

Unexpected value: {expressionType}

Error message

Unexpected value: {expressionType}

What it means

CoherenceFilterExpressionConverter.convert maps Spring AI Filter.Expression types to Coherence Filters and ends with a default branch that throws IllegalStateException for any unsupported type. It is an exhaustiveness guard: the switch only supports EQ, NE, GT, GTE, LT, LTE, IN, NIN, AND, OR.

Source

Thrown at vector-stores/spring-ai-coherence-store/src/main/java/org/springframework/ai/vectorstore/coherence/CoherenceFilterExpressionConverter.java:72

	private Filter<?> convert(Expression expression) {
		if (expression.type() == ExpressionType.NOT) {
			return convert(FilterHelper.negate(expression));
		}
		Assert.state(expression.right() != null, "expression is expected to have a right operand");
		return switch (expression.type()) {
			case EQ -> Filters.equal(extractor(expression.left()), value(expression.right()));
			case NE -> Filters.notEqual(extractor(expression.left()), value(expression.right()));
			case GT -> Filters.greater(extractor(expression.left()), value(expression.right()));
			case GTE -> Filters.greaterEqual(extractor(expression.left()), value(expression.right()));
			case LT -> Filters.less(extractor(expression.left()), value(expression.right()));
			case LTE -> Filters.lessEqual(extractor(expression.left()), value(expression.right()));
			case IN -> Filters.in(extractor(expression.left()), ((List) value(expression.right())).toArray());
			case NIN ->
				Filters.not(Filters.in(extractor(expression.left()), ((List) value(expression.right())).toArray()));
			case AND -> Filters.all(convert(expression.left()), convert(expression.right()));
			case OR -> Filters.any(convert(expression.left()), convert(expression.right()));
			default -> throw new IllegalStateException("Unexpected value: " + expression.type());
		};
	}

	private ValueExtractor extractor(Operand op) {
		return new ChainedExtractor(new UniversalExtractor<>("metadata"), new UniversalExtractor<>(((Key) op).key()));
	}

	private <T> T value(Operand op) {
		return (T) ((Value) op).value();
	}

}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Rewrite NOT via De Morgan's laws using AND/OR/NE (e.g. not(a.eq(x)) -> a.ne(x)).
  2. Replace CONTAINS with EQ on scalar fields or IN over known values; move array-membership filtering to application code.
  3. Extend the converter's switch to map the missing type to Coherence Filters (e.g. Filters.not(...)).

Example fix

// before
Filter.expr("metadata.role").not("admin")
// after
Filter.expr("metadata.role").ne("admin")
Defensive patterns

Strategy: validation

Validate before calling

Set<Filter.ExpressionType> OK = Set.of(EQ, NE, GT, GTE, LT, LTE, IN, NIN, AND, OR);
void check(Filter.Expression e) {
  if (!OK.contains(e.type())) throw new IllegalArgumentException("Unsupported for Coherence: " + e.type());
  if (e.left() instanceof Filter.Expression l) check(l);
  if (e.right() instanceof Filter.Expression r) check(r);
}

Type guard

boolean coherenceSafe(Filter.Expression e) {
  return !(e.type() == NOT || e.type() == CONTAINS || e.type() == CONTAINS_KEY);
}

Try / catch

try {
  vectorStore.similaritySearch(request);
} catch (IllegalStateException e) {
  if (e.getMessage().startsWith("Unexpected value")) {
    // fall back to unfiltered search + client-side filtering
  }
}

Prevention

When it happens

Trigger: Passing a filter containing NOT or CONTAINS/CONTAINS_KEY (or any type outside the switch) to a Coherence-backed vector store similaritySearch or delete.

Common situations: Using Filter.not(...) built from FilterExpressionTextParser output; porting filters from stores that support contains on arrays; expanding a dynamic filter DSL with new operators not yet mapped.

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/61e41fe441c48e50. Report an issue: GitHub.