{"record":{"id":"a570eb49b26d2958","repo":"spring-projects/spring-ai","slug":"unsupported-value-in-in-value-list-only-supports","errorCode":null,"errorMessage":"Unsupported value in IN value list. Only supports String or Number","messagePattern":"Unsupported value in IN value list\\. Only supports String or Number","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java","lineNumber":186,"sourceCode":"\t\t\tif (firstValue instanceof String) {\n\t\t\t\t// If the first value is a string, then all values should be strings\n\t\t\t\tList<String> stringValues = new ArrayList<>();\n\t\t\t\tfor (Object valueObj : valueList) {\n\t\t\t\t\tstringValues.add(valueObj.toString());\n\t\t\t\t}\n\t\t\t\treturn io.qdrant.client.ConditionFactory.matchKeywords(identifier, stringValues);\n\t\t\t}\n\t\t\telse if (firstValue instanceof Number) {\n\t\t\t\t// If the first value is a number, then all values should be numbers\n\t\t\t\tList<Long> longValues = new ArrayList<>();\n\t\t\t\tfor (Object valueObj : valueList) {\n\t\t\t\t\tLong longValue = Long.parseLong(valueObj.toString());\n\t\t\t\t\tlongValues.add(longValue);\n\t\t\t\t}\n\t\t\t\treturn io.qdrant.client.ConditionFactory.matchValues(identifier, longValues);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new RuntimeException(\"Unsupported value in IN value list. Only supports String or Number\");\n\t\t\t}\n\t\t}\n\t\tthrow new RuntimeException(\n\t\t\t\t\"Unsupported value type for IN condition. Only supports non-empty List of String or Number\");\n\n\t}\n\n\tprotected Condition buildNInCondition(Key key, Value value) {\n\t\tif (value.value() instanceof List valueList && !valueList.isEmpty()) {\n\t\t\tObject firstValue = valueList.get(0);\n\t\t\tString identifier = doKey(key);\n\n\t\t\tif (firstValue instanceof String) {\n\t\t\t\t// If the first value is a string, then all values should be strings\n\t\t\t\tList<String> stringValues = new ArrayList<>();\n\t\t\t\tfor (Object valueObj : valueList) {\n\t\t\t\t\tstringValues.add(valueObj.toString());\n\t\t\t\t}","sourceCodeStart":168,"sourceCodeEnd":204,"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#L168-L204","documentation":"buildInCondition expands an IN comparison into a Qdrant matchValues condition. It requires a non-empty List whose elements are all String or all Number; a list containing any other element type (Boolean, null, mixed with objects) throws this RuntimeException.","triggerScenarios":"Filter.builder().in(\"tags\", List.of(\"a\", true)).build() or in(\"ids\", List.of(1, null)) — an IN list containing a non-String/non-Number element.","commonSituations":"Dynamic filters assembled from user-supplied multi-select inputs where a boolean/null slips into the list; mixed-type lists from deserialized JSON arrays.","solutions":["Sanitize the list before building: keep only Strings or only Numbers (in(\"tags\", List.of(\"a\",\"b\"))).","Convert booleans/dates to strings or numbers so all elements share one supported type.","Filter out nulls and normalize mixed-type collections before calling filterExpression().","If heterogeneous membership checks are needed, split into multiple IN filters combined with OR."],"exampleFix":"// before\nFilter.builder().in(\"category\", List.of(\"news\", true)).build();\n// after\nFilter.builder().in(\"category\", List.of(\"news\", \"true\")).build();","handlingStrategy":"validation","validationCode":"static List<Object> sanitizeInList(List<?> in) {\n    if (in == null) return List.of();\n    return in.stream()\n        .filter(v -> v != null)\n        .map(v -> (v instanceof Boolean b) ? b.toString() : (v instanceof Number || v instanceof String) ? v : v.toString())\n        .toList();\n}","typeGuard":"static boolean isInListCompatible(List<?> list) {\n    return list != null && !list.isEmpty()\n        && list.stream().allMatch(v -> v instanceof String || v instanceof Number);\n}","tryCatchPattern":"try {\n    vectorStore.similaritySearch(req);\n} catch (RuntimeException ex) {\n    if (ex.getMessage() != null && ex.getMessage().contains(\"Unsupported value in IN value list\")) {\n        throw new IllegalArgumentException(\"IN list elements must be all String or all Number\", ex);\n    }\n    throw ex;\n}","preventionTips":["Keep IN lists homogeneous: all Strings or all Numbers.","Strip nulls and normalize booleans/dates to strings/numbers before building.","Sanitize multi-select UI inputs before turning them into filters."],"tags":["qdrant","filter-expression","in","list","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-23T08:17:48.524Z"}