spring-projects/spring-ai · error · IllegalArgumentException
Expected a Key operand but got:
Error message
Expected a Key operand but got:
What it means
The evaluator's metadataValue() expects the left operand of a comparison to be a Filter.Key naming a metadata field. If the operand is a Value (or any other Operand type), it throws IllegalArgumentException including the actual class name. Metadata comparisons must be keyed on document metadata, not constants on the left side.
Solutions
- Make the metadata field a Filter.Key on the left side and the constant a Filter.Value on the right.
- If using the text parser, write the filter as 'color == \'green\'' not '\'green\' == color'.
- Inspect the parsed expression tree (Filter.Expression#left) to confirm the left operand is a Key.
Example fix
// before
new Filter.Expression(EQ, new Filter.Value("green"), new Filter.Key("color"));
// after
new Filter.Expression(EQ, new Filter.Key("color"), new Filter.Value("green")); Defensive patterns
Strategy: validation
Validate before calling
boolean leftIsKey(Filter.Expression e) {
return e != null && e.left() instanceof Filter.Key;
} Type guard
Filter.Key asKey(Filter.Operand op) { return op instanceof Filter.Key k ? k : null; } Try / catch
try {
vectorStore.similaritySearch(request);
} catch (IllegalArgumentException ex) {
if (ex.getMessage().startsWith("Expected a Key operand")) {
// swap operands: metadata key must be on the left
} else throw ex;
} Prevention
- Write text filters as field == value, never value == field
- In builder code, put Filter.Key first and Filter.Value second
When it happens
Trigger: A Filter.Expression like EQ(value, value) or EQ(value, key) — i.e. a Filter.Value used where a Filter.Key is expected — evaluated against SimpleVectorStore.
Common situations: Swapping operand order when hand-building expressions; string filters like "'green' == color" instead of "color == 'green'"; code generation producing reversed comparisons.
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:
- Cannot compare values of incompatible types
- Cannot compare values of types
- Expected a List value for
- Expression of type requires a left operand
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/878589c6fae86745.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SimpleVectorStoreFilterExpressionEvaluator.java:155
}
return right;
}
/**
* Extracts the metadata value for the given {@link Filter.Key} operand. Outer quotes
* ({@code "..."} or {@code '...'}) are stripped from the key name to match the format
* used by {@link FilterExpressionBuilder} and the text parser.
*/
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());
}
/**
* Extracts the constant value from a {@link Filter.Value} operand. {@link Date}
* instances are formatted to their ISO-8601 UTC string so they can be compared
* directly with metadata strings stored in the same format.
*/
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());
}
/**
* Compares two values. Numbers are promoted to {@code double} to allow cross-type
* numeric comparison (e.g. {@code Integer} vs {@code Double}). All otherView on GitHub (pinned to 98a7beda4f)