spring-projects/spring-ai · error · RuntimeException

Non AND/OR/ISNULL/ISNOTNULL expression must have Value…

Error message

Non AND/OR/ISNULL/ISNOTNULL expression must have Value right argument!

What it means

AbstractFilterExpressionConverter.convertOperand validates the shape of every Filter.Expression it converts: any type other than NOT/AND/OR (and except ISNULL/ISNOTNULL which may omit a right side) must have a Filter.Value as its right argument. If not, it throws this RuntimeException, guarding against malformed filter trees being rendered into provider query strings.

Solutions

  1. Ensure every binary expression has new Filter.Value(...) as its right operand and Filter.Key on the left
  2. Validate the tree before converting: for each non-NOT/AND/OR/ISNULL/ISNOTNULL node assert right() instanceof Filter.Value
  3. Use Filter.builder() or the text parser instead of manual construction so trees are well-formed
  4. Catch RuntimeException around convertExpression and report the offending expression

Example fix

// before
new Filter.Expression(ExpressionType.GT, new Filter.Key("price"), new Filter.Key("min"));
// after
new Filter.Expression(ExpressionType.GT, new Filter.Key("price"), new Filter.Value(minPrice));
Defensive patterns

Strategy: validation

Validate before calling

boolean wellFormed(Filter.Expression e) { return e.type() == ExpressionType.NOT || e.type() == ExpressionType.AND || e.type() == ExpressionType.OR || e.type() == ExpressionType.ISNULL || e.type() == ExpressionType.ISNOTNULL || e.right() instanceof Filter.Value; }

Try / catch

try { s = converter.convertExpression(expr); }
catch (RuntimeException e) { throw new IllegalStateException("Malformed filter tree: " + e.getMessage(), e); }

Prevention

When it happens

Trigger: Converting a Filter.Expression whose type is e.g. GT/LT/EQ but whose right() is null, a Filter.Key, or another expression; typically from hand-built or deserialized filter trees, reached via convertOperand recursion, doNot, or doGroup.

Common situations: Mistakenly swapping left/right when constructing expressions (key on the right); building binary expressions with only a left side; third-party code producing non-canonical trees.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/50331b7f251578f1. Report an issue: GitHub.

Appendix: source

Thrown at spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/converter/AbstractFilterExpressionConverter.java:110

	 * @param operand the operand to convert
	 * @param context the context to append the string representation to
	 */
	protected void convertOperand(Operand operand, StringBuilder context) {

		if (operand instanceof Filter.Group group) {
			this.doGroup(group, context);
		}
		else if (operand instanceof Filter.Key key) {
			this.doKey(key, context);
		}
		else if (operand instanceof Filter.Value value) {
			this.doValue(value, context);
		}
		else if (operand instanceof Filter.Expression expression) {
			if ((expression.type() != ExpressionType.NOT && expression.type() != ExpressionType.AND
					&& expression.type() != ExpressionType.OR) && !(expression.right() instanceof Filter.Value)
					&& !(expression.type() == ExpressionType.ISNULL || expression.type() == ExpressionType.ISNOTNULL)) {
				throw new RuntimeException("Non AND/OR/ISNULL/ISNOTNULL expression must have Value right argument!");
			}
			if (expression.type() == ExpressionType.NOT) {
				this.doNot(expression, context);
			}
			else {
				this.doExpression(expression, context);
			}
		}
	}

	/**
	 * Convert the given expression into a string representation.
	 * @param expression the expression to convert
	 * @param context the context to append the string representation to
	 */
	protected void doNot(Filter.Expression expression, StringBuilder context) {
		// Default behavior is to convert the NOT expression into its semantically
		// equivalent negation expression.

View on GitHub (pinned to 98a7beda4f)