spring-projects/spring-ai · error · IllegalArgumentException

Cannot compare values of types %s and %s

Error message

Cannot compare values of types %s and %s

What it means

compare() throws this when neither null/Number/equals handling applies and at least one side is not Comparable — for example comparing metadata against a Boolean or a Map/List where equality also failed. Unlike error 628 this is not a ClassCastException but a static inability to order the two values.

Source

Thrown at vector-stores/spring-ai-s3-vector-store/src/main/java/org/springframework/ai/vectorstore/s3/S3VectorStoreFilterExpressionEvaluator.java:146

		}
		if (metaVal instanceof Number n1 && filterVal instanceof Number n2) {
			return Double.compare(n1.doubleValue(), n2.doubleValue());
		}
		if (Objects.equals(metaVal, filterVal)) {
			return 0;
		}
		if (metaVal instanceof Comparable comparable && filterVal instanceof Comparable) {
			try {
				@SuppressWarnings("unchecked")
				int result = comparable.compareTo(filterVal);
				return result;
			}
			catch (ClassCastException ex) {
				throw new IllegalArgumentException("Cannot compare values of incompatible types %s and %s"
					.formatted(metaVal.getClass().getName(), filterVal.getClass().getName()), ex);
			}
		}
		throw new IllegalArgumentException("Cannot compare values of types %s and %s"
			.formatted(metaVal.getClass().getName(), filterVal.getClass().getName()));
	}

	private List<?> asList(Object value, Filter.Expression expression) {
		if (value instanceof List<?> list) {
			return list;
		}
		throw new IllegalArgumentException(
				"Expected a List value for %s expression but got: %s".formatted(expression.type(), value));
	}

}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use EQ/NE (or IN/NIN) for non-Comparable values like Booleans, Lists, or objects instead of GT/LT range operators.
  2. Ensure the metadata field holds a scalar Comparable type (String or Number) consistent with the filter literal type.
  3. Fix ingestion so structured values (lists/maps) are not stored in fields used for range filtering.
  4. Pre-filter such documents client-side after retrieval instead of relying on the evaluator's comparison.

Example fix

// before
new Filter.Expression(Filter.ExpressionType.GT, new Filter.Key("enabled"), new Filter.Value(false))
// after
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("enabled"), new Filter.Value(true))
Defensive patterns

Strategy: validation

Validate before calling

static void assertOrderable(Filter.ExpressionType t, Object filterValue) {
  if (List.of(GT, GTE, LT, LTE).contains(t) && !(filterValue instanceof Number || filterValue instanceof String || filterValue instanceof Date))
    throw new IllegalStateException("Range operators need Number/String/Date, got " + filterValue.getClass());
}

Type guard

static boolean orderable(Object v) { return v instanceof Number || v instanceof String || v instanceof Comparable; }

Try / catch

try { store.similaritySearch(req); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Cannot compare values of types")) { /* switch to EQ/NE or fix metadata scalar type */ } }

Prevention

When it happens

Trigger: Using an ordering operator (GT/GTE/LT/LTE) or IN/NIN whose element comparison lands here: e.g. metadata field holds a Boolean/List while the filter literal is a String/Number, or metadata contains a nested object that is not Comparable, during S3VectorStore similaritySearch.

Common situations: Ordering filters on boolean fields (booleans are not Comparable against other types here); metadata ingested as JSON arrays/objects then used in range filters; type drift between ingestion and query time.

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/d9fbeff6a98cad6a. Report an issue: GitHub.