spring-projects/spring-ai · error · RuntimeException

Unsupported value type for GT condition. Only supports Numbe

Error message

Unsupported value type for GT condition. Only supports Number

What it means

Filter translation guard in QdrantFilterExpressionConverter.buildGtCondition: a GT (greater-than) comparison was given a non-Number right-hand value; Qdrant range conditions only support numeric comparisons.

Source

Thrown at vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java:129

				.build());
		}
		else if (value.value() instanceof Number valueNum) {
			long lValue = Long.parseLong(valueNum.toString());
			Condition condition = io.qdrant.client.ConditionFactory.match(identifier, lValue);
			return io.qdrant.client.ConditionFactory.filter(Filter.newBuilder().addMustNot(condition).build());
		}

		throw new IllegalArgumentException("Invalid value type for NEQ. Can either be a string or Number");

	}

	protected Condition buildGtCondition(Key key, Value value) {
		String identifier = doKey(key);
		if (value.value() instanceof Number valueNum) {
			Double dvalue = Double.parseDouble(valueNum.toString());
			return io.qdrant.client.ConditionFactory.range(identifier, Range.newBuilder().setGt(dvalue).build());
		}
		throw new RuntimeException("Unsupported value type for GT condition. Only supports Number");

	}

	protected Condition buildLtCondition(Key key, Value value) {
		String identifier = doKey(key);
		if (value.value() instanceof Number valueNum) {
			Double dvalue = Double.parseDouble(valueNum.toString());
			return io.qdrant.client.ConditionFactory.range(identifier, Range.newBuilder().setLt(dvalue).build());
		}
		throw new RuntimeException("Unsupported value type for LT condition. Only supports Number");

	}

	protected Condition buildGteCondition(Key key, Value value) {
		String identifier = doKey(key);
		if (value.value() instanceof Number valueNum) {
			Double dvalue = Double.parseDouble(valueNum.toString());
			return io.qdrant.client.ConditionFactory.range(identifier, Range.newBuilder().setGte(dvalue).build());

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use a numeric value for the GT comparison
  2. Store the field as a number in Qdrant and filter numerically

Example fix

// before
Filter.builder().gt("createdAt", "2024-01-01").build();
// after
Filter.builder().gt("createdAt", Instant.parse("2024-01-01T00:00:00Z").getEpochSecond()).build();
Defensive patterns

Strategy: validation

Validate before calling

static void checkRangeOperand(Object v) {
    if (!(v instanceof Number)) {
        throw new IllegalArgumentException("Range operand (GT/GTE/LT/LTE) must be a Number, got " + (v == null ? "null" : v.getClass()));
    }
}

Type guard

static boolean isRangeCompatible(Object v) {
    return v instanceof Number;
}

Try / catch

try {
    vectorStore.similaritySearch(req);
} catch (RuntimeException ex) {
    if (ex.getMessage() != null && ex.getMessage().contains("GT condition. Only supports Number")) {
        throw new IllegalArgumentException("GT requires a numeric operand; store dates as epoch numbers", ex);
    }
    throw ex;
}

Prevention

When it happens

Trigger: Filter.builder().gt("createdAt", "2024-01-01").build() or gt("price", null) — any GT whose Value is a String date, Boolean, etc., instead of a Number.

Common situations: Filtering date fields with ISO-8601 strings (very common) — Qdrant ranges are numeric, so dates must be stored as epoch numbers.

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