{"record":{"id":"878589c6fae86745","repo":"spring-projects/spring-ai","slug":"expected-a-key-operand-but-got","errorCode":null,"errorMessage":"Expected a Key operand but got: ","messagePattern":"Expected a Key operand but got: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SimpleVectorStoreFilterExpressionEvaluator.java","lineNumber":155,"sourceCode":"\t\t}\n\t\treturn right;\n\t}\n\n\t/**\n\t * Extracts the metadata value for the given {@link Filter.Key} operand. Outer quotes\n\t * ({@code \"...\"} or {@code '...'}) are stripped from the key name to match the format\n\t * used by {@link FilterExpressionBuilder} and the text parser.\n\t */\n\tprivate @Nullable Object metadataValue(Filter.Operand operand, Map<String, Object> metadata) {\n\t\tif (operand instanceof Filter.Key key) {\n\t\t\tString k = key.key();\n\t\t\tif (k.length() >= 2\n\t\t\t\t\t&& ((k.startsWith(\"\\\"\") && k.endsWith(\"\\\"\")) || (k.startsWith(\"'\") && k.endsWith(\"'\")))) {\n\t\t\t\tk = k.substring(1, k.length() - 1);\n\t\t\t}\n\t\t\treturn metadata.get(k);\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Expected a Key operand but got: \" + operand.getClass().getName());\n\t}\n\n\t/**\n\t * Extracts the constant value from a {@link Filter.Value} operand. {@link Date}\n\t * instances are formatted to their ISO-8601 UTC string so they can be compared\n\t * directly with metadata strings stored in the same format.\n\t */\n\tprivate Object filterValue(Filter.Operand operand) {\n\t\tif (operand instanceof Filter.Value filterValue) {\n\t\t\tObject value = filterValue.value();\n\t\t\treturn (value instanceof Date date) ? DATE_FORMATTER.format(date.toInstant()) : value;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Expected a Value operand but got: \" + operand.getClass().getName());\n\t}\n\n\t/**\n\t * Compares two values. Numbers are promoted to {@code double} to allow cross-type\n\t * numeric comparison (e.g. {@code Integer} vs {@code Double}). All other","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SimpleVectorStoreFilterExpressionEvaluator.java#L137-L173","documentation":"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.","triggerScenarios":"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.","commonSituations":"Swapping operand order when hand-building expressions; string filters like \"'green' == color\" instead of \"color == 'green'\"; code generation producing reversed comparisons.","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."],"exampleFix":"// before\nnew Filter.Expression(EQ, new Filter.Value(\"green\"), new Filter.Key(\"color\"));\n// after\nnew Filter.Expression(EQ, new Filter.Key(\"color\"), new Filter.Value(\"green\"));","handlingStrategy":"validation","validationCode":"boolean leftIsKey(Filter.Expression e) {\n    return e != null && e.left() instanceof Filter.Key;\n}","typeGuard":"Filter.Key asKey(Filter.Operand op) { return op instanceof Filter.Key k ? k : null; }","tryCatchPattern":"try {\n    vectorStore.similaritySearch(request);\n} catch (IllegalArgumentException ex) {\n    if (ex.getMessage().startsWith(\"Expected a Key operand\")) {\n        // swap operands: metadata key must be on the left\n    } else throw ex;\n}","preventionTips":["Write text filters as field == value, never value == field","In builder code, put Filter.Key first and Filter.Value second"],"tags":["vector-store","filter-expression","operand-type","spring-ai"],"backgroundTag":"invalid-argument-value","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}