spring-projects/spring-ai · error · IllegalArgumentException
Expected a Key operand but got:
Error message
Expected a Key operand but got:
What it means
metadataValue resolves the left side of a comparison and requires a Filter.Key operand naming a metadata field. Any other operand type (Value, Group, or raw Expression) used where a field name is expected throws IllegalArgumentException with the operand's class name.
Source
Thrown at vector-stores/spring-ai-s3-vector-store/src/main/java/org/springframework/ai/vectorstore/s3/S3VectorStoreFilterExpressionEvaluator.java:108
private Filter.Operand right(Filter.Expression expression) {
Filter.Operand right = expression.right();
if (right == null) {
throw new IllegalArgumentException(
"Expression of type %s requires a right operand".formatted(expression.type()));
}
return right;
}
private @Nullable Object metadataValue(Filter.Operand operand, Map<String, Object> metadata) {
if (operand instanceof Filter.Key key) {
String k = key.key();
if (k.length() >= 2
&& ((k.startsWith("\"") && k.endsWith("\"")) || (k.startsWith("'") && k.endsWith("'")))) {
k = k.substring(1, k.length() - 1);
}
return metadata.get(k);
}
throw new IllegalArgumentException("Expected a Key operand but got: " + operand.getClass().getName());
}
private Object filterValue(Filter.Operand operand) {
if (operand instanceof Filter.Value filterValue) {
Object value = filterValue.value();
return (value instanceof Date date) ? DATE_FORMATTER.format(date.toInstant()) : value;
}
throw new IllegalArgumentException("Expected a Value operand but got: " + operand.getClass().getName());
}
private int compare(@Nullable Object metaVal, @Nullable Object filterVal) {
if (metaVal == null && filterVal == null) {
return 0;
}
if (metaVal == null) {
return -1;
}
if (filterVal == null) {View on GitHub (pinned to 98a7beda4f)
Solutions
- Flip the expression so the metadata field (Filter.Key) is on the left and the literal (Filter.Value) on the right, inverting the operator if needed (a > b -> b < a).
- Use Filter.expr("field == value") string parsing, which always produces key-left form.
- Add a sanity check that left() instanceof Filter.Key before submitting the filter.
- If the right side is the Key and left is the Value, swap operands and swap EQ<->NE or LT<->GT etc.
Example fix
// before
new Filter.Expression(Filter.ExpressionType.GT, new Filter.Value(5), new Filter.Key("age"))
// after
new Filter.Expression(Filter.ExpressionType.LT, new Filter.Key("age"), new Filter.Value(5)) Defensive patterns
Strategy: validation
Validate before calling
static void requireKeyLeft(Filter.Expression e) {
if (List.of(EQ, NE, GT, GTE, LT, LTE, IN, NIN, ISNULL, ISNOTNULL).contains(e.type())
&& !(e.left() instanceof Filter.Key)) throw new IllegalStateException("Left side must be a metadata Key");
} Type guard
static boolean isKeyLeft(Filter.Expression e) { return e.left() instanceof Filter.Key; } Try / catch
try { store.similaritySearch(req); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Expected a Key operand")) { /* swap operands and invert operator */ } } Prevention
- Always write filters as field OP literal, never literal OP field.
- Use Filter.expr(...) which normalizes to key-left form.
- Add a lint check that rejects Value-on-left comparisons.
When it happens
Trigger: Putting a Filter.Value or Group on the left side of an EQ/NE/GT/LT/IN/ISNULL expression (i.e. comparing in the wrong direction, e.g. Value on the left, Key on the right), then evaluating via S3VectorStore similaritySearch.
Common situations: Reversed comparisons like 5 > age written as filters; filters copied from textual DSLs that permit either side; code generators that place operands inconsistently.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Expected a Value operand but got:
- Unsupported operand type:
- Expression of type %s requires a right operand
- Cannot compare values of incompatible types %s and %s
- Not supported expression type:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/83821475aba3176c.
Report an issue: GitHub.