apache/cassandra · error · InvalidRequestException

ANN ordering by vector requires all restricted column(s) to…

Error message

ANN ordering by vector requires all restricted column(s) to be indexed

What it means

Thrown when an ANN query combines the ANN ordering with additional filtering (non-ANN restrictions on other columns) where at least one filtered column lacks a supporting index. Cassandra requires every non-ANN restriction in an ANN query to be served by an index so the filtering happens in the indexed/ANN path, not as a post-filter scan.

Solutions

  1. Create indexes on every non-ANN column used in the WHERE clause of the ANN query.
  2. Remove the unindexed filter predicates and post-filter results client-side after the ANN search.
  3. Ensure partition key restrictions are direct equality (not filtering) and clustering filters are indexed.
  4. Denormalize the data so metadata filters become partition-key equalities in a query table.

Example fix

// before
SELECT * FROM t WHERE cat = 'a' ORDER BY v ANN OF [0.1,0.2]; -- cat not indexed
// after
CREATE INDEX ON t (cat);
SELECT * FROM t WHERE cat = 'a' ORDER BY v ANN OF [0.1,0.2];
Defensive patterns

Strategy: validation

Validate before calling

// every non-ANN WHERE column in an ANN query must be indexed
List<String> nonAnn = whereColumns.stream().filter(c -> !c.equals(annColumn)).collect(toList());
for (String c : nonAnn)
    if (!isIndexed(table, c)) throw new IllegalStateException("ANN query filter column not indexed: " + c);

Try / catch

try { session.execute(query); } catch (InvalidRequestException e) { if (e.getMessage().contains("all restricted column(s) to be indexed")) postFilterClientSide(); else throw e; }

Prevention

When it happens

Trigger: `SELECT * FROM t WHERE cat = 'a' ORDER BY v ANN OF [...]` where v is indexed but cat is not; ANN query with clustering/partition filtering columns that are not indexed; combining ANN with several predicates, some unindexed.

Common situations: Hybrid vector+metadata search where metadata columns were never indexed; indexes exist for some but not all filter columns; adding new filter columns to an existing ANN query without indexing them.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/3f14ab1fb7ffad5c. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java:337

                throw invalidRequest("Non PRIMARY KEY columns found in where clause: %s ",
                                     Joiner.on(", ").join(nonPrimaryKeyColumns));
            }

            Optional<SingleRestriction> annRestriction = Streams.stream(nonPrimaryKeyRestrictions)
                                                                .filter(SingleRestriction::isANN)
                                                                .findFirst();
            if (annRestriction.isPresent())
            {
                // If there is an ANN restriction then it must be for a vector<float, n> column, and it must have an index
                ColumnMetadata annColumn = annRestriction.get().firstColumn();

                if (!annColumn.type.isVector() || !(((VectorType<?>)annColumn.type).elementType instanceof FloatType))
                    throw invalidRequest(ANN_ONLY_SUPPORTED_ON_VECTOR_MESSAGE);
                if (indexRegistry == null || indexRegistry.listIndexes().stream().noneMatch(i -> i.dependsOn(annColumn)))
                    throw invalidRequest(ANN_REQUIRES_INDEX_MESSAGE);
                // We do not allow ANN queries using partition key restrictions that need filtering
                if (partitionKeyRestrictions.needFiltering())
                    throw invalidRequest(ANN_REQUIRES_INDEXED_FILTERING_MESSAGE);
                // We do not allow ANN query filtering using non-indexed columns
                List<ColumnMetadata> nonAnnColumns = Streams.stream(nonPrimaryKeyRestrictions)
                                                            .filter(r -> !r.isANN())
                                                            .map(SingleRestriction::firstColumn)
                                                            .collect(Collectors.toList());
                List<ColumnMetadata> clusteringColumns = clusteringColumnsRestrictions.columns();
                if (!nonAnnColumns.isEmpty() || !clusteringColumns.isEmpty())
                {
                    List<ColumnMetadata> nonIndexedColumns = Stream.concat(nonAnnColumns.stream(), clusteringColumns.stream())
                                                                   .filter(c -> indexRegistry.listIndexes().stream().noneMatch(i -> i.dependsOn(c)))
                                                                   .collect(Collectors.toList());
                    if (!nonIndexedColumns.isEmpty())
                    {
                        // restrictions on non-clustering columns, or clusterings that still need filtering, are invalid
                        if (!clusteringColumns.containsAll(nonIndexedColumns)
                                || partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents()
                                || clusteringColumnsRestrictions.needFiltering())
                            throw invalidRequest(StatementRestrictions.ANN_REQUIRES_INDEXED_FILTERING_MESSAGE);

View on GitHub (pinned to 88fd0f6a0e)