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

  1. Sanitize the list before building: keep only Strings or only Numbers (in("tags", List.of("a","b"))).
  2. Convert booleans/dates to strings or numbers so all elements share one supported type.
  3. Filter out nulls and normalize mixed-type collections before calling filterExpression().
  4. 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

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


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)