spring-projects/spring-ai · error · RuntimeException

Not supported expression type:

Error message

Not supported expression type:

What it means

The Typesense filter expression converter maps each Filter.ExpressionType to a filter_by operator string. If the expression type falls outside the supported switch cases (AND, OR, EQ, NE, LT, LTE, GT, GTE, IN, NIN), getOperationSymbol throws a RuntimeException with 'Not supported expression type:'. Typesense's filter_by syntax cannot represent the unsupported operator.

Solutions

  1. Check which expression type you are passing and rewrite the filter using only supported operators (AND, OR, EQ, NE, LT, LTE, GT, GTE, IN, NIN).
  2. Upgrade or patch spring-ai-typesense-store if a newly added Filter.ExpressionType should be supported.
  3. Pre-validate expression types before calling the converter/store and reject unsupported ones with a clear error.

Example fix

// before
var expr = new Filter.Expression(Filter.ExpressionType.CONTAINS, new Key("title"), new Value("ai"));
// after
var expr = new Filter.Expression(Filter.ExpressionType.EQ, new Key("title"), new Value("ai"));
Defensive patterns

Strategy: validation

Validate before calling

Set<Filter.ExpressionType> SUPPORTED = Set.of(AND, OR, EQ, NE, LT, LTE, GT, GTE, IN, NIN); if (!SUPPORTED.contains(expr.type())) throw new IllegalArgumentException("Typesense cannot filter on " + expr.type());

Try / catch

try { converter.convertExpression(expr); } catch (RuntimeException e) { if (e.getMessage().startsWith("Not supported expression type")) { /* fall back to in-memory filtering or another store */ } else throw e; }

Prevention

When it happens

Trigger: Passing a Filter.Expression with an operator Typesense doesn't map (currently none outside the standard set, but any new/unsupported type such as an unrecognized enum value reaches the default branch) via TypesenseVectorStore similaritySearch or the converter directly.

Common situations: Spring AI version upgrades introducing new Filter.ExpressionType values not yet handled by the converter; custom expression types from wrapper libraries; misuse of the converter API with hand-built expressions.

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/0895460339ad8a0e. Report an issue: GitHub.

Appendix: source

Thrown at vector-stores/spring-ai-typesense-store/src/main/java/org/springframework/ai/vectorstore/typesense/TypesenseFilterExpressionConverter.java:52

		this.convertOperand(exp.left(), context);
		context.append(getOperationSymbol(exp));
		this.convertOperand(exp.right(), context);
	}

	private String getOperationSymbol(Filter.Expression exp) {
		return switch (exp.type()) {
			case AND -> " && ";
			case OR -> " || ";
			case EQ -> " "; // in typesense "EQ" operator looks like -> country:USA
			case NE -> " != ";
			case LT -> " < ";
			case LTE -> " <= ";
			case GT -> " > ";
			case GTE -> " >= ";
			case IN -> " "; // in typesense "IN" operator looks like -> country: [USA, UK]
			case NIN -> " != "; // in typesense "NIN" operator looks like -> country:
			// !=[USA, UK]
			default -> throw new RuntimeException("Not supported expression type:" + exp.type());
		};
	}

	@Override
	protected void doGroup(Filter.Group group, StringBuilder context) {
		this.convertOperand(new Filter.Expression(Filter.ExpressionType.AND, group.content(), group.content()),
				context); // trick
	}

	@Override
	protected void doKey(Filter.Key key, StringBuilder context) {
		var identifier = (hasOuterQuotes(key.key())) ? removeOuterQuotes(key.key()) : key.key();
		// Typesense field names are bare identifiers in filter_by syntax
		// (field_name:value) with no escaping mechanism. Validate that the
		// identifier contains only safe characters to prevent filter injection.
		for (int i = 0; i < identifier.length(); i++) {
			char c = identifier.charAt(i);
			if (!Character.isLetterOrDigit(c) && c != '_' && c != '.' && c != '-') {

View on GitHub (pinned to 98a7beda4f)