spring-projects/spring-ai · error · RuntimeException

Similarity threshold filtering requires all vectors to be…

Error message

Similarity threshold filtering requires all vectors to be normalized, see the forcedNormalization parameter for this Vector store. Also only COSINE and DOT distance types are supported.

What it means

OracleVectorStore.doSimilaritySearch throws this RuntimeException when a similarity threshold is used but the store cannot guarantee meaningful threshold math: either forcedNormalization was not enabled or the configured distance type is not COSINE or DOT. Threshold filtering requires normalized vectors so distance can be derived from similarity.

Solutions

  1. Set forcedNormalization(true) in the OracleVectorStore builder and re-ingest vectors so they are normalized.
  2. Switch the distance type to COSINE or DOT via OracleVectorStoreDistanceType.
  3. Set the search threshold to 0 if you cannot normalize vectors, and filter results in application code instead.
  4. Verify existing rows are normalized (L2 norm = 1) before enabling threshold filtering.

Example fix

// before
OracleVectorStore.builder(jdbcTemplate, embeddingModel)
    .distanceType(OracleVectorStoreDistanceType.EUCLIDEAN)
    .build();
request.withSimilarityThreshold(0.7); // throws
// after
OracleVectorStore.builder(jdbcTemplate, embeddingModel)
    .forcedNormalization(true)
    .distanceType(OracleVectorStoreDistanceType.COSINE)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

boolean thresholdUsable = store.isForcedNormalization()
    && (store.getDistanceType() == OracleVectorStoreDistanceType.COSINE
     || store.getDistanceType() == OracleVectorStoreDistanceType.DOT);
if (!thresholdUsable) request = request.withSimilarityThreshold(0);

Try / catch

try {
    vectorStore.similaritySearch(request.withSimilarityThreshold(0.75));
} catch (RuntimeException e) {
    if (e.getMessage().contains("Similarity threshold filtering requires")) {
        results = vectorStore.similaritySearch(request.withSimilarityThreshold(0)); // post-filter in app
    }
}

Prevention

When it happens

Trigger: Calling similaritySearch(SearchRequest) with a similarityThreshold > 0 on an OracleVectorStore built without forcedNormalization=true, or with OracleVectorStoreDistanceType.EUCLIDEAN / other non-COSINE/DOT distance.

Common situations: Default builder config (no forcedNormalization) combined with a nonzero threshold; switching distance type to EUCLIDEAN for legacy data while keeping threshold logic; documents ingested without L2 normalization.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/OracleVectorStore.java:418

						select id, content, metadata, embedding, %sVECTOR_DISTANCE(embedding, ?, %s)%s as distance
						from %s
						%sorder by distance
						fetch EXACT first %d rows only""",
						this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? "(1+" : "",
						this.distanceType.name(),
						this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? ")/2" : "",
						this.tableName, jsonPathFilter, request.getTopK());

				if (logger.isDebugEnabled()) {
					logger.debug("SQL query: " + sql);
				}

				return this.jdbcTemplate.query(sql, new DocumentRowMapper(), embeddingVector);
			}
			else {
				if (!this.forcedNormalization || (this.distanceType != OracleVectorStoreDistanceType.COSINE
						&& this.distanceType != OracleVectorStore.OracleVectorStoreDistanceType.DOT)) {
					throw new RuntimeException(
							"Similarity threshold filtering requires all vectors to be normalized, see the forcedNormalization parameter for this Vector store. Also only COSINE and DOT distance types are supported.");
				}

				final double distance = this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT
						? (1d - request.getSimilarityThreshold()) * 2d - 1d : 1d - request.getSimilarityThreshold();

				if (StringUtils.hasText(nativeFilterExpression)) {
					jsonPathFilter = " and " + nativeFilterExpression;
				}

				final String sql = this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT
						? (this.searchAccuracy == DEFAULT_SEARCH_ACCURACY
								? String.format(
										"""
												select id, content, metadata, embedding, (1+VECTOR_DISTANCE(embedding, ?, DOT))/2 as distance
												from %s
												where VECTOR_DISTANCE(embedding, ?, DOT) <= ?%s
												order by distance

View on GitHub (pinned to 98a7beda4f)