spring-projects/spring-ai · error · IllegalArgumentException

Invalid value type for NEQ. Can either be a string or Number

Error message

Invalid value type for NEQ. Can either be a string or Number

What it means

Filter translation guard in QdrantFilterExpressionConverter.buildNeCondition: a FilterExpression NEQ comparison was given a right-hand value that is neither a String nor a Number, which Qdrant match conditions cannot represent.

Source

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

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

	}

	protected Condition buildNeCondition(Key key, Value value) {
		String identifier = doKey(key);
		if (value.value() instanceof String valueStr) {
			return io.qdrant.client.ConditionFactory.filter(Filter.newBuilder()
				.addMustNot(io.qdrant.client.ConditionFactory.matchKeyword(identifier, valueStr))
				.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());

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use a String or Number value in the NEQ expression
  2. Convert boolean/other types to a supported representation before filtering

Example fix

// before
Filter.builder().ne("deleted", true).build();
// after
Filter.builder().ne("deleted", "true").build();
Defensive patterns

Strategy: validation

Validate before calling

static void checkNeOperand(Object v) {
    if (!(v instanceof String) && !(v instanceof Number)) {
        throw new IllegalArgumentException("NE operand must be String or Number, got " + (v == null ? "null" : v.getClass()));
    }
}

Type guard

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

Try / catch

try {
    vectorStore.similaritySearch(req);
} catch (IllegalArgumentException ex) {
    if (ex.getMessage() != null && ex.getMessage().startsWith("Invalid value type for NEQ")) {
        throw new IllegalArgumentException("NE operand must be String or Number", ex);
    }
    throw ex;
}

Prevention

When it happens

Trigger: filterExpression(NE) whose right Value is a Boolean, null, date, or List — e.g. Filter.builder().ne("status", true).build() sent to the Qdrant store.

Common situations: Same as EQ: boolean or date metadata fields being negated in a filter; typical when metadata was ingested with heterogeneous JSON types.

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