apache/cassandra · error · InvalidRequestException
Cannot create more than one storage-attached index on the sa
Error message
Cannot create more than one storage-attached index on the same column:
What it means
SAI (Storage-Attached Index) rejects CREATE INDEX when another SAI index already exists on the same target column. During index option validation the metadata's existing indexes are filtered to SAI indexes, their targets are parsed, and if more than one matches the requested target the request is invalid. This prevents duplicate indexes that would waste space and confuse query planning.
Source
Thrown at src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java:274
}
Pair<ColumnMetadata, IndexTarget.Type> target = TargetParser.parse(metadata, targetColumn);
if (target == null)
{
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())View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Use CREATE CUSTOM INDEX IF NOT EXISTS to skip creation when the index already exists.
- Query system_schema.indexes to check for an existing SAI index on the column before creating one.
- If different options are needed, DROP the existing index first, then create the new one.
- Index a different column or use a different index implementation if you genuinely need a second index.
Example fix
// before CREATE CUSTOM INDEX ON ks.tbl (col) USING 'StorageAttachedIndex'; // after CREATE CUSTOM INDEX IF NOT EXISTS ON ks.tbl (col) USING 'StorageAttachedIndex';
Defensive patterns
Strategy: validation
Validate before calling
SELECT column_name FROM system_schema.indexes WHERE keyspace_name='ks' AND table_name='tbl' AND options CONTAINS 'StorageAttachedIndex'; // then check column in app code before CREATE INDEX
Try / catch
try { session.execute(createIndexCql); } catch (InvalidRequestException e) { if (e.getMessage().startsWith("Cannot create more than one storage-attached index")) log.info("index already exists, ignoring"); else throw e; } Prevention
- Always use IF NOT EXISTS for idempotent schema migrations
- Keep one SAI index definition per column in migration scripts
- Introspect system_schema.indexes before DDL
When it happens
Trigger: Calling CREATE CUSTOM INDEX ... USING 'StorageAttachedIndex' on a column that already has an SAI index (the new index is already present in metadata.indexes, so the count of matching parsed targets exceeds 1).
Common situations: Re-running a schema migration/idempotent CREATE INDEX script without IF NOT EXISTS; creating a second index on the same column hoping for different analyzer options; copying index DDL across keyspaces.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Unsupported type:
- category %s not found in %s
- 'Get CIDR groups for IP' operation not supported by %s
- ACCESS TO DATACENTERS operations not supported by AllowAllNe
- frozen<> is only allowed on collections, tuples, and user-de
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/e07b378a32671e3c.
Report an issue: GitHub.