spring-projects/spring-ai · error · RuntimeException

Not supported expression type: {expressionType}

Error message

Not supported expression type: {expressionType}

What it means

CouchbaseAiSearchFilterExpressionConverter.getOperationSymbol maps supported filter types to N1QL operator strings. Types outside GT, GTE, LT, LTE, IN, NIN (and the equality/comparison set handled before it) hit the default branch and throw this RuntimeException. It is called from doExpression during filter-to-N1QL conversion.

Source

Thrown at vector-stores/spring-ai-couchbase-store/src/main/java/org/springframework/ai/vectorstore/couchbase/CouchbaseAiSearchFilterExpressionConverter.java:57

		}
		else {
			context.append("NULL");
		}
	}

	private String getOperationSymbol(Expression exp) {
		return switch (exp.type()) {
			case AND -> " AND ";
			case OR -> " 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 doKey(Key key, StringBuilder context) {
		context.append("metadata.");
		var identifier = key.key();
		// Couchbase N1QL/SQL++ uses backtick-quoted identifiers.
		// Within backticks, the only character needing escaping is the backtick
		// itself (doubled as ``).
		context.append('`');
		for (int i = 0; i < identifier.length(); i++) {
			char c = identifier.charAt(i);
			if (c == '`') {
				context.append("``");
			}
			else {
				context.append(c);

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Rewrite NOT using De Morgan's laws with supported operators (NE, AND, OR).
  2. Replace CONTAINS/CONTAINS_KEY with EQ/IN where possible, or filter array metadata client-side after retrieval.
  3. Extend getOperationSymbol to emit N1QL for the missing operator (e.g. " NOT IN " patterns already exist for NIN).

Example fix

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

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try {
  vectorStore.similaritySearch(request);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("Not supported expression type")) {
    // rewrite filter without the unsupported operator and retry
  }
}

Prevention

When it happens

Trigger: Passing a filter expression with an unsupported type (e.g. NOT, CONTAINS) to a Couchbase vector store search or delete; the N1QL converter has no operator symbol for it.

Common situations: Filters parsed from text with negation operators; reusing filter DSLs from other vector stores; metadata array searches using CONTAINS.

Related errors


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