spring-projects/spring-ai · error · UnsupportedOperationException

Tag operand not supported

Error message

Tag operand {0} not supported

What it means

tagValueDelimiter maps a filter expression type to the RediSearch TAG-list delimiter: IN uses ' | ', EQ uses ' '. Any other operand (NE, GT, LT, NIN, etc.) against a TAG field cannot be expressed with tag delimiters, so the converter throws UnsupportedOperationException 'Tag operand {0} not supported'.

Solutions

  1. Use only EQ or IN on TAG fields (e.g. status eq "done" or status in ["a","b"])
  2. Re-register the field as MetadataField.numeric(...) if range comparisons are required (values must be numeric)
  3. Implement inequality by excluding in application code after an IN query
  4. Model negation as IN of the allowed set instead of NE/NIN on a tag

Example fix

// before
Filter.expr("status").ne("done"); // TAG field
// after
Filter.expr("status").in(List.of("open", "pending"));
Defensive patterns

Strategy: validation

Validate before calling

if (field.fieldType() == TAG && !(expr.type() == EQ || expr.type() == IN)) throw new UnsupportedOperationException("TAG fields support only EQ/IN, got: " + expr.type());

Type guard

static boolean isTagSafeOperand(ExpressionType t) { return t == ExpressionType.EQ || t == ExpressionType.IN; }

Try / catch

try { vectorStore.similaritySearch(request); } catch (UnsupportedOperationException e) { if (e.getMessage().contains("Tag operand")) { /* rewrite filter to IN/EQ */ } else throw e; }

Prevention

When it happens

Trigger: Using ne(), gt(), lt(), gte(), lte() or nin() on a metadata field registered as MetadataField.tag(...), e.g. Filter.expr("status").ne("done").

Common situations: Assuming TAG fields behave like text/numeric fields and applying range or inequality filters; migrating filters written for NUMERIC fields onto TAG fields.

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/65da6d557fbb54c2. Report an issue: GitHub.

Appendix: source

Thrown at vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverter.java:185

	 * {@code [}, {@code ]}, {@code -}, and {@code '}.
	 */
	private String escapeTagValue(String value) {
		StringBuilder sb = new StringBuilder(value.length());
		for (int i = 0; i < value.length(); i++) {
			char c = value.charAt(i);
			switch (c) {
				case '\\', '$', '|', '{', '}', '(', ')', '[', ']', '-', '\'' -> sb.append('\\').append(c);
				default -> sb.append(c);
			}
		}
		return sb.toString();
	}

	private String tagValueDelimiter(Expression expression) {
		return switch (expression.type()) {
			case IN -> " | ";
			case EQ -> " ";
			default -> throw new UnsupportedOperationException(
					MessageFormat.format("Tag operand {0} not supported", expression.type()));
		};
	}

	private Numeric numeric(Expression expression, Value value) {
		return switch (expression.type()) {
			case EQ -> new Numeric(inclusive(value), inclusive(value));
			case GT -> new Numeric(exclusive(value), NumericBoundary.POSITIVE_INFINITY);
			case GTE -> new Numeric(inclusive(value), NumericBoundary.POSITIVE_INFINITY);
			case LT -> new Numeric(NumericBoundary.NEGATIVE_INFINITY, exclusive(value));
			case LTE -> new Numeric(NumericBoundary.NEGATIVE_INFINITY, inclusive(value));
			default -> throw new UnsupportedOperationException(
					MessageFormat.format("Expression type {0} not supported for numeric fields", expression.type()));
		};
	}

	private NumericBoundary inclusive(Value value) {
		if (!(value.value() instanceof Number)) {

View on GitHub (pinned to 98a7beda4f)