{"record":{"id":"3aa73867cca97a68","repo":"spring-projects/spring-ai","slug":"numeric-value-must-be-a-number","errorCode":null,"errorMessage":"Numeric value must be a Number","messagePattern":"Numeric value must be a Number","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverter.java","lineNumber":204,"sourceCode":"\t\t\t\t\tMessageFormat.format(\"Tag operand {0} not supported\", expression.type()));\n\t\t};\n\t}\n\n\tprivate Numeric numeric(Expression expression, Value value) {\n\t\treturn switch (expression.type()) {\n\t\t\tcase EQ -> new Numeric(inclusive(value), inclusive(value));\n\t\t\tcase GT -> new Numeric(exclusive(value), NumericBoundary.POSITIVE_INFINITY);\n\t\t\tcase GTE -> new Numeric(inclusive(value), NumericBoundary.POSITIVE_INFINITY);\n\t\t\tcase LT -> new Numeric(NumericBoundary.NEGATIVE_INFINITY, exclusive(value));\n\t\t\tcase LTE -> new Numeric(NumericBoundary.NEGATIVE_INFINITY, inclusive(value));\n\t\t\tdefault -> throw new UnsupportedOperationException(\n\t\t\t\t\tMessageFormat.format(\"Expression type {0} not supported for numeric fields\", expression.type()));\n\t\t};\n\t}\n\n\tprivate NumericBoundary inclusive(Value value) {\n\t\tif (!(value.value() instanceof Number)) {\n\t\t\tthrow new IllegalArgumentException(\"Numeric value must be a Number\");\n\t\t}\n\t\treturn new NumericBoundary(value.value(), false);\n\t}\n\n\tprivate NumericBoundary exclusive(Value value) {\n\t\tif (!(value.value() instanceof Number)) {\n\t\t\tthrow new IllegalArgumentException(\"Numeric value must be a Number\");\n\t\t}\n\t\treturn new NumericBoundary(value.value(), true);\n\t}\n\n\t@Override\n\tprotected void doSingleValue(Object value, StringBuilder context) {\n\t\temitJsonValue(value, context);\n\t}\n\n\trecord Numeric(NumericBoundary lower, NumericBoundary upper) {\n","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverter.java#L186-L222","documentation":"In RedisFilterExpressionConverter.inclusive(), the boundary value of a numeric filter expression must be an instance of java.lang.Number. If value.value() is a String, Boolean or any other object, the converter throws this IllegalArgumentException because it cannot build a Redis NumericBoundary from it. This guards the numeric range construction for EQ/GTE/LTE-style boundaries.","triggerScenarios":"Passing a non-numeric value in a filter expression against a numeric field, e.g. eq(\"year\", \"2024\") (String instead of int), or a metadata value that was stored/typed as a String but used in a numeric comparison.","commonSituations":"Reading filter values from JSON/HTTP request parameters where everything arrives as a String; metadata written with String values then used in numeric filters; auto-boxing assumptions after refactoring a field from numeric to text.","solutions":["Pass a Java Number type (Integer, Long, Double, Float) as the filter value for numeric fields","Parse String input explicitly before building the expression, e.g. Integer.parseInt(value)","Verify the metadata field type at write time so numeric fields always hold numeric values"],"exampleFix":"// before\nFilter.Expression expr = b.eq(\"year\", \"2024\").build();\n// after\nFilter.Expression expr = b.eq(\"year\", Integer.parseInt(\"2024\")).build();","handlingStrategy":"type-guard","validationCode":"if (!(rawValue instanceof Number)) throw new IllegalArgumentException(\"Numeric filter value must be a Number: \" + rawValue);","typeGuard":"static boolean isNumericValue(Object v) { return v instanceof Number; }","tryCatchPattern":"try { store.similaritySearch(req); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"must be a Number\")) { /* fix filter value type */ } }","preventionTips":["Use typed builder values (Integer/Double) not Strings","Parse user input to numbers before building filters","Keep metadata field types consistent at write time"],"tags":["redis","vector-store","filter-expression","type-mismatch"],"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"}