{"record":{"id":"fdb237b7901b5541","repo":"spring-projects/spring-ai","slug":"invalid-value-type-for-eq-can-either-be-a-string","errorCode":null,"errorMessage":"Invalid value type for EQ. Can either be a string or Number","messagePattern":"Invalid value type for EQ\\. Can either be a string or Number","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java","lineNumber":102,"sourceCode":"\t\t\tcase LT -> buildLtCondition(key, value);\n\t\t\tcase LTE -> buildLteCondition(key, value);\n\t\t\tcase IN -> buildInCondition(key, value);\n\t\t\tcase NIN -> buildNInCondition(key, value);\n\t\t\tdefault -> throw new RuntimeException(\"Unsupported expression type: \" + type);\n\t\t};\n\t}\n\n\tprotected Condition buildEqCondition(Key key, Value value) {\n\t\tString identifier = doKey(key);\n\t\tif (value.value() instanceof String valueStr) {\n\t\t\treturn io.qdrant.client.ConditionFactory.matchKeyword(identifier, valueStr);\n\t\t}\n\t\telse if (value.value() instanceof Number valueNum) {\n\t\t\tlong lValue = Long.parseLong(valueNum.toString());\n\t\t\treturn io.qdrant.client.ConditionFactory.match(identifier, lValue);\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\"Invalid value type for EQ. Can either be a string or Number\");\n\n\t}\n\n\tprotected Condition buildNeCondition(Key key, Value value) {\n\t\tString identifier = doKey(key);\n\t\tif (value.value() instanceof String valueStr) {\n\t\t\treturn io.qdrant.client.ConditionFactory.filter(Filter.newBuilder()\n\t\t\t\t.addMustNot(io.qdrant.client.ConditionFactory.matchKeyword(identifier, valueStr))\n\t\t\t\t.build());\n\t\t}\n\t\telse if (value.value() instanceof Number valueNum) {\n\t\t\tlong lValue = Long.parseLong(valueNum.toString());\n\t\t\tCondition condition = io.qdrant.client.ConditionFactory.match(identifier, lValue);\n\t\t\treturn io.qdrant.client.ConditionFactory.filter(Filter.newBuilder().addMustNot(condition).build());\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\"Invalid value type for NEQ. Can either be a string or Number\");\n","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java#L84-L120","documentation":"buildEqCondition translates an EQ comparison into a Qdrant match condition, accepting only String or Number values; anything else (Boolean, null, Date, collection) throws this IllegalArgumentException. Qdrant's match() field conditions only support those scalar types directly.","triggerScenarios":"filterExpression(EQ) where the right-hand Value wraps a Boolean (e.g. eq(\"isActive\", true)), null, a date object, or a List — e.g. Filter.builder().eq(\"active\", true).build() passed to the Qdrant store.","commonSituations":"Document metadata contains booleans or dates; developers write eq(\"flag\", true) expecting Qdrant to handle it. Also common when metadata values are auto-extracted from JSON where a field is an object/array rather than a scalar.","solutions":["Replace boolean equality with a string/int: eq(\"active\", \"true\") or eq(\"active\", 1), and store metadata accordingly.","Convert dates to epoch-millis Numbers before filtering.","For list membership use IN instead of EQ.","Normalize document metadata on ingestion so filtered fields are always String or Number."],"exampleFix":"// before\nFilter.builder().eq(\"isActive\", true).build();\n// after\nFilter.builder().eq(\"isActive\", \"true\").build();","handlingStrategy":"type-guard","validationCode":"static void checkEqOperand(Object v) {\n    if (!(v instanceof String) && !(v instanceof Number)) {\n        throw new IllegalArgumentException(\"EQ operand must be String or Number, got \" + (v == null ? \"null\" : v.getClass()));\n    }\n}","typeGuard":"static boolean isEqCompatible(Object v) {\n    return v instanceof String || v instanceof Number;\n}","tryCatchPattern":"try {\n    vectorStore.similaritySearch(req);\n} catch (IllegalArgumentException ex) {\n    if (ex.getMessage() != null && ex.getMessage().startsWith(\"Invalid value type for EQ\")) {\n        throw new IllegalArgumentException(\"Metadata field must be String or Number for EQ\", ex);\n    }\n    throw ex;\n}","preventionTips":["Never filter on boolean or date metadata with EQ; encode as String/Number at ingestion.","Validate metadata types when documents are added to the store.","Document per-field metadata types for your vector-store schema."],"tags":["qdrant","filter-expression","eq","type-mismatch","spring-ai"],"backgroundTag":"type-mismatch","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}