{"record":{"id":"77c2155d3e8902b0","repo":"apache/cassandra","slug":"analysis-options-are-not-supported-on-primary-key","errorCode":null,"errorMessage":"Analysis options are not supported on primary key columns, but found ","messagePattern":"Analysis options are not supported on primary key columns, but found ","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java","lineNumber":280,"sourceCode":"            throw new InvalidRequestException(\"Failed to retrieve target column for: \" + targetColumn);\n        }\n\n        // In order to support different index targets on non-frozen map, ie. KEYS, VALUE, ENTRIES, we need to put index\n        // name as part of index file name instead of column name. We only need to check that the target is different\n        // between indexes. This will only allow indexes in the same column with a different IndexTarget.Type.\n        //\n        // Note that: \"metadata.indexes\" already includes current index\n        if (metadata.indexes.stream().filter(index -> index.getIndexClassName().equals(StorageAttachedIndex.class.getName()))\n                            .map(index -> TargetParser.parse(metadata, index.options.get(IndexTarget.TARGET_OPTION_NAME)))\n                            .filter(Objects::nonNull).filter(t -> t.equals(target)).count() > 1)\n        {\n            throw new InvalidRequestException(\"Cannot create more than one storage-attached index on the same column: \" + target.left);\n        }\n\n        Map<String, String> analysisOptions = AbstractAnalyzer.getAnalyzerOptions(options);\n        if (target.left.isPrimaryKeyColumn() && !analysisOptions.isEmpty())\n        {\n            throw new InvalidRequestException(ANALYSIS_ON_KEY_COLUMNS_MESSAGE + new CqlBuilder().append(analysisOptions));\n        }\n\n        IndexTermType indexTermType = IndexTermType.create(target.left, metadata.partitionKeyColumns(), target.right);\n        AbstractAnalyzer.fromOptions(indexTermType, analysisOptions);\n        IndexWriterConfig config = IndexWriterConfig.fromOptions(null, indexTermType, options);\n\n        // If we are indexing map entries we need to validate the subtypes\n        if (indexTermType.isComposite())\n        {\n            for (IndexTermType subType : indexTermType.subTypes())\n            {\n                if (!SUPPORTED_TYPES.contains(subType.asCQL3Type()) && !subType.isFrozen())\n                    throw new InvalidRequestException(\"Unsupported type: \" + subType.asCQL3Type());\n            }\n        }\n        else if (!SUPPORTED_TYPES.contains(indexTermType.asCQL3Type()) && !indexTermType.isFrozen())\n        {\n            throw new InvalidRequestException(\"Unsupported type: \" + indexTermType.asCQL3Type());","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java#L262-L298","documentation":"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).","triggerScenarios":"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).","commonSituations":"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.","solutions":["Remove all analyzer options from the CREATE INDEX statement for primary key columns.","Create the SAI index without an analyzer class; key columns are matched exactly.","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."],"exampleFix":"// before\nCREATE CUSTOM INDEX ON ks.tbl (partition_col) USING 'StorageAttachedIndex'\n  WITH OPTIONS = {'case_sensitive': 'false'};\n// after\nCREATE CUSTOM INDEX ON ks.tbl (partition_col) USING 'StorageAttachedIndex';","handlingStrategy":"validation","validationCode":"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');","typeGuard":null,"tryCatchPattern":"catch (InvalidRequestException e) { if (e.getMessage().contains(\"Analysis options are not supported on primary key columns\")) { // strip options and retry without them } }","preventionTips":["Never copy analyzer options onto indexes of key columns","Gate option emission in DDL generators on the column's role","Keep key-column indexes option-free"],"tags":["cassandra","sai","primary-key","invalid-options"],"backgroundTag":"unsupported-config-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}