spring-projects/spring-ai · error · IllegalArgumentException

Expression of type %s requires a right operand

Error message

Expression of type %s requires a right operand

What it means

The evaluator's right() accessor throws IllegalArgumentException when a Filter.Expression lacks a right operand where one is required. Binary operators (AND, OR, EQ, NE, GT, GTE, LT, LTE, IN, NIN) all dereference right(), so a null right operand is rejected as a malformed filter.

Source

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

			}
			case ISNULL -> metadataValue(left(expression), metadata) == null;
			case ISNOTNULL -> metadataValue(left(expression), metadata) != null;
		};
	}

	private Filter.Operand left(Filter.Expression expression) {
		Filter.Operand left = expression.left();
		if (left == null) {
			throw new IllegalArgumentException(
					"Expression of type %s requires a left operand".formatted(expression.type()));
		}
		return left;
	}

	private Filter.Operand right(Filter.Expression expression) {
		Filter.Operand right = expression.right();
		if (right == null) {
			throw new IllegalArgumentException(
					"Expression of type %s requires a right operand".formatted(expression.type()));
		}
		return right;
	}

	private @Nullable Object metadataValue(Filter.Operand operand, Map<String, Object> metadata) {
		if (operand instanceof Filter.Key key) {
			String k = key.key();
			if (k.length() >= 2
					&& ((k.startsWith("\"") && k.endsWith("\"")) || (k.startsWith("'") && k.endsWith("'")))) {
				k = k.substring(1, k.length() - 1);
			}
			return metadata.get(k);
		}
		throw new IllegalArgumentException("Expected a Key operand but got: " + operand.getClass().getName());
	}

	private Object filterValue(Filter.Operand operand) {

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Always supply a Filter.Value for comparison operators; use the string "null" sentinel or a dedicated metadata flag if you mean 'missing'.
  2. For null checks use ISNULL/ISNOTNULL expression types, which do not require a right operand.
  3. Skip building the expression entirely when the value is absent, rather than passing null.
  4. Validate the filter tree (all binary nodes have both operands) before calling similaritySearch.

Example fix

// before
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("genre"), null)
// after
new Filter.Expression(Filter.ExpressionType.ISNULL, new Filter.Key("genre"), null)
Defensive patterns

Strategy: validation

Validate before calling

static boolean needsRight(Filter.ExpressionType t) { return t != NOT && t != ISNULL && t != ISNOTNULL; }
static void requireRight(Filter.Expression e) { if (needsRight(e.type()) && e.right() == null) throw new IllegalStateException("Missing right operand"); }

Type guard

static boolean hasRight(Filter.Expression e) { return e.right() != null; }

Try / catch

try { store.similaritySearch(req); } catch (IllegalArgumentException e) { if (e.getMessage().endsWith("requires a right operand")) { /* drop or rebuild the incomplete predicate */ } }

Prevention

When it happens

Trigger: Building a comparison like new Filter.Expression(EQ, new Filter.Key("genre"), null) or a binary AND/OR missing its second child, then running S3VectorStore similaritySearch that evaluates the filter against ListVectors metadata.

Common situations: Partial filter assembly (value forgotten); ISNULL/ISNOTNULL-style expressions ported from other stores where the right side is null by convention — here ISNULL/ISNOTNULL read only the left side, but comparisons with null right side throw; dynamic filter builders that skip unset values.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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