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
- Use only EQ or IN on TAG fields (e.g. status eq "done" or status in ["a","b"])
- Re-register the field as MetadataField.numeric(...) if range comparisons are required (values must be numeric)
- Implement inequality by excluding in application code after an IN query
- 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
- Restrict TAG-field filters to eq() and in()
- Use NUMERIC fields for range/inequality filters
- Document which metadata keys are TAG vs NUMERIC in your schema
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
- Field type not supported
- Not allowed filter identifier name:
- Cannot compare values of incompatible types
- Cannot compare values of types
- Either vectorStore or jedisClient must be provided
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)