apache/cassandra · error · InvalidRequestException

ANN ordering by vector requires the column to be indexed

Error message

ANN ordering by vector requires the column to be indexed

What it means

Thrown when an ANN ordering is requested on a valid float vector column that has no index attached (or no index registry is available). ANN search is executed through a vector index (SAI/COSINE etc.); without one, Cassandra cannot perform approximate nearest-neighbor search.

Solutions

  1. Create a vector index: CREATE INDEX ON t (v) USING 'sai' (or the storage-attached index form for vectors).
  2. Wait for the index to become queryable/build-complete before running ANN queries.
  3. Confirm the index depends on the exact ANN column (not another column) via schema metadata.
  4. If no index is desired, fetch rows and compute similarity client-side instead of ANN.

Example fix

// before
SELECT * FROM t ORDER BY v ANN OF [0.1, 0.2]; -- no index on v
// after
CREATE INDEX ann_idx ON t (v) USING 'sai' WITH OPTIONS = { 'similarity_function': 'cosine' };
SELECT * FROM t ORDER BY v ANN OF [0.1, 0.2] LIMIT 5;
Defensive patterns

Strategy: validation

Validate before calling

boolean hasVectorIndex = table.getIndexes().values().stream()
    .anyMatch(i -> i.getTarget().equals(vectorColumn));
if (!hasVectorIndex) throw new IllegalStateException("create a vector index on " + vectorColumn + " before ANN queries");

Try / catch

try { session.execute(annQuery); } catch (InvalidRequestException e) { if (e.getMessage().contains("requires the column to be indexed")) { createVectorIndex(); waitForIndex(); retry(); } else throw e; }

Prevention

When it happens

Trigger: `SELECT * FROM t ORDER BY v ANN OF [0.1,...]` where no `CREATE INDEX ... USING ... ON t(v)` exists, or the index was dropped, or the statement runs in a context without an index registry.

Common situations: Freshly created table queried before index build completed; index dropped during maintenance; tests with un-indexed tables; index created on a different vector column.

Related errors


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

Appendix: source

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

                Collection<ColumnIdentifier> nonPrimaryKeyColumns =
                        ColumnMetadata.toIdentifiers(nonPrimaryKeyRestrictions.columns());

                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)

View on GitHub (pinned to 88fd0f6a0e)