apache/cassandra · error · InvalidRequestException

Analysis options are not supported on primary key columns…

Error message

Analysis options are not supported on primary key columns, but found 

What it means

SAI does not allow analyzer (text-processing) options on primary key columns. validateOptions extracts analyzer options from the CREATE INDEX options and, if the index target is a partition/clustering column and any analysis options are present, throws this InvalidRequestException (ANALYSIS_ON_KEY_COLUMNS_MESSAGE plus the offending options).

Solutions

  1. Remove all analyzer options from the CREATE INDEX statement for primary key columns.
  2. Create the SAI index without an analyzer class; key columns are matched exactly.
  3. If case-insensitive matching on a key column is needed, model the data differently (e.g., a denormalized lowercase column) instead of using analyzer options.

Example fix

// before
CREATE CUSTOM INDEX ON ks.tbl (partition_col) USING 'StorageAttachedIndex'
  WITH OPTIONS = {'case_sensitive': 'false'};
// after
CREATE CUSTOM INDEX ON ks.tbl (partition_col) USING 'StorageAttachedIndex';
Defensive patterns

Strategy: validation

Validate before calling

const isPrimaryKey = schema.partitionKeys.concat(schema.clusteringKeys).some(k => k.name === targetColumn); if (isPrimaryKey && Object.keys(indexOptions).length > 0) throw new Error('no analyzer options allowed on key columns');

Try / catch

catch (InvalidRequestException e) { if (e.getMessage().contains("Analysis options are not supported on primary key columns")) { // strip options and retry without them } }

Prevention

When it happens

Trigger: CREATE CUSTOM INDEX with options like 'case_sensitive': 'false' or 'normalize_upper': 'true' (any NonTokenizingOptions keys) on a PRIMARY KEY column (partition key or clustering column).

Common situations: Adding case_sensitive/normalize options to an index on a clustering column copied from a text-column index definition; templated DDL generators applying analyzer options to all indexed columns.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java:280

            throw new InvalidRequestException("Failed to retrieve target column for: " + targetColumn);
        }

        // In order to support different index targets on non-frozen map, ie. KEYS, VALUE, ENTRIES, we need to put index
        // name as part of index file name instead of column name. We only need to check that the target is different
        // between indexes. This will only allow indexes in the same column with a different IndexTarget.Type.
        //
        // Note that: "metadata.indexes" already includes current index
        if (metadata.indexes.stream().filter(index -> index.getIndexClassName().equals(StorageAttachedIndex.class.getName()))
                            .map(index -> TargetParser.parse(metadata, index.options.get(IndexTarget.TARGET_OPTION_NAME)))
                            .filter(Objects::nonNull).filter(t -> t.equals(target)).count() > 1)
        {
            throw new InvalidRequestException("Cannot create more than one storage-attached index on the same column: " + target.left);
        }

        Map<String, String> analysisOptions = AbstractAnalyzer.getAnalyzerOptions(options);
        if (target.left.isPrimaryKeyColumn() && !analysisOptions.isEmpty())
        {
            throw new InvalidRequestException(ANALYSIS_ON_KEY_COLUMNS_MESSAGE + new CqlBuilder().append(analysisOptions));
        }

        IndexTermType indexTermType = IndexTermType.create(target.left, metadata.partitionKeyColumns(), target.right);
        AbstractAnalyzer.fromOptions(indexTermType, analysisOptions);
        IndexWriterConfig config = IndexWriterConfig.fromOptions(null, indexTermType, options);

        // If we are indexing map entries we need to validate the subtypes
        if (indexTermType.isComposite())
        {
            for (IndexTermType subType : indexTermType.subTypes())
            {
                if (!SUPPORTED_TYPES.contains(subType.asCQL3Type()) && !subType.isFrozen())
                    throw new InvalidRequestException("Unsupported type: " + subType.asCQL3Type());
            }
        }
        else if (!SUPPORTED_TYPES.contains(indexTermType.asCQL3Type()) && !indexTermType.isFrozen())
        {
            throw new InvalidRequestException("Unsupported type: " + indexTermType.asCQL3Type());

View on GitHub (pinned to 88fd0f6a0e)