spring-projects/spring-ai · error · RuntimeException
Unsupported value in IN value list. Only supports String or…
Error message
Unsupported value in IN value list. Only supports String or Number
What it means
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.
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.
Example fix
// before
Filter.builder().in("category", List.of("news", true)).build();
// after
Filter.builder().in("category", List.of("news", "true")).build(); Defensive patterns
Strategy: validation
Validate before calling
static List<Object> sanitizeInList(List<?> in) {
if (in == null) return List.of();
return in.stream()
.filter(v -> v != null)
.map(v -> (v instanceof Boolean b) ? b.toString() : (v instanceof Number || v instanceof String) ? v : v.toString())
.toList();
} Type guard
static boolean isInListCompatible(List<?> list) {
return list != null && !list.isEmpty()
&& list.stream().allMatch(v -> v instanceof String || v instanceof Number);
} Try / catch
try {
vectorStore.similaritySearch(req);
} catch (RuntimeException ex) {
if (ex.getMessage() != null && ex.getMessage().contains("Unsupported value in IN value list")) {
throw new IllegalArgumentException("IN list elements must be all String or all Number", ex);
}
throw ex;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Dynamic filters assembled from user-supplied multi-select inputs where a boolean/null slips into the list; mixed-type lists from deserialized JSON arrays.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Unsupported value type for IN condition. Only supports…
- Invalid value type for EQ. Can either be a string or Number
- Invalid value type for NEQ. Can either be a string or Number
- Non AND/OR/NOT expression must have Value right argument!
- Unsupported expression type:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/a570eb49b26d2958.
Report an issue: GitHub.
Appendix: source
Thrown at vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java:186
if (firstValue instanceof String) {
// If the first value is a string, then all values should be strings
List<String> stringValues = new ArrayList<>();
for (Object valueObj : valueList) {
stringValues.add(valueObj.toString());
}
return io.qdrant.client.ConditionFactory.matchKeywords(identifier, stringValues);
}
else if (firstValue instanceof Number) {
// If the first value is a number, then all values should be numbers
List<Long> longValues = new ArrayList<>();
for (Object valueObj : valueList) {
Long longValue = Long.parseLong(valueObj.toString());
longValues.add(longValue);
}
return io.qdrant.client.ConditionFactory.matchValues(identifier, longValues);
}
else {
throw new RuntimeException("Unsupported value in IN value list. Only supports String or Number");
}
}
throw new RuntimeException(
"Unsupported value type for IN condition. Only supports non-empty List of String or Number");
}
protected Condition buildNInCondition(Key key, Value value) {
if (value.value() instanceof List valueList && !valueList.isEmpty()) {
Object firstValue = valueList.get(0);
String identifier = doKey(key);
if (firstValue instanceof String) {
// If the first value is a string, then all values should be strings
List<String> stringValues = new ArrayList<>();
for (Object valueObj : valueList) {
stringValues.add(valueObj.toString());
}View on GitHub (pinned to 98a7beda4f)