{"record":{"id":"8a067de938de7927","repo":"apache/cassandra","slug":"secondary-indexes-are-not-supported-on-collections","errorCode":null,"errorMessage":"Secondary indexes are not supported on collections containing durations","messagePattern":"Secondary indexes are not supported on collections containing durations","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java","lineNumber":268,"sourceCode":"    private void validateIndexTarget(TableMetadata table, IndexMetadata.Kind kind, IndexTarget target, IndexAttributes attrs)\n    {\n        ColumnMetadata column = table.getColumn(target.column);\n\n        if (null == column)\n            throw ire(COLUMN_DOES_NOT_EXIST, target.column);\n\n        AbstractType<?> baseType = column.type.unwrap();\n\n        boolean isNonSAIIndex = !isSAIIndex(attrs);\n\n        // TODO: this check needs to be removed with CASSANDRA-20235\n        if ((kind == IndexMetadata.Kind.CUSTOM))\n            validateCustomIndexColumnName(target.column.toString());\n\n        if (column.type.referencesDuration())\n        {\n            if (column.type.isCollection())\n                throw ire(COLLECTIONS_WITH_DURATIONS_NOT_SUPPORTED);\n\n            if (column.type.isTuple())\n                throw ire(TUPLES_WITH_DURATIONS_NOT_SUPPORTED);\n\n            if (column.type.isUDT())\n                throw  ire(UDTS_WITH_DURATIONS_NOT_SUPPORTED);\n\n            throw ire(DURATIONS_NOT_SUPPORTED);\n        }\n\n        if (table.isCompactTable())\n        {\n            TableMetadata.CompactTableMetadata compactTable = (TableMetadata.CompactTableMetadata) table;\n            if (column.isPrimaryKeyColumn())\n                throw new InvalidRequestException(PRIMARY_KEY_IN_COMPACT_STORAGE);\n            if (compactTable.compactValueColumn.equals(column))\n                throw new InvalidRequestException(COMPACT_COLUMN_IN_COMPACT_STORAGE);\n        }","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java#L250-L286","documentation":"Cassandra rejects creating a secondary index on a collection column (list/set/map) whose element, key, or value type is or contains `duration`. Durations are not orderable/comparable in the way index backends require, so indexing collections containing them is disallowed. The check fires in validateIndexTarget when the target column type references duration and is a collection.","triggerScenarios":"CREATE INDEX / CREATE CUSTOM INDEX whose target is a list<duration>, set<duration>, map<duration, X>, map<X, duration>, or a frozen/multi-cell collection nesting duration (including via UDT/tuple elements), e.g. `CREATE INDEX ON t (full(map_col))` where map_col is frozen<map<text, duration>>.","commonSituations":"Schema migrations adding duration fields to tracked interval data then trying to index the collection for queries; using duration inside map values for time-budget per-key lookups; copying an index DDL from a table where the column was timestamp.","solutions":["Remove duration from the collection's type parameters (use timestamp/bigint for instants, or store total milliseconds).","Denormalize: store the duration-derived value as a separate indexed column (e.g. bigint seconds) alongside the collection.","Query without a secondary index: filter client-side or model the data so lookups use partition/clustering keys instead."],"exampleFix":"// before\nCREATE INDEX ON events (tags); // tags: map<text, duration>\n// after\nALTER TABLE events ADD tag_seconds map<text, bigint>;\nCREATE INDEX ON events (tag_seconds);","handlingStrategy":"validation","validationCode":"function canIndexCollection(type) {\n  return type.isCollection() ? !type.referencesDuration() : !type.referencesDuration();\n}\nif (column.type.isCollection() && column.type.referencesDuration()) throw new Error('unindexable: collection contains duration');","typeGuard":"const isIndexableCollection = (t) => t.isCollection() && !t.referencesDuration();","tryCatchPattern":"try { session.execute(\"CREATE INDEX ON t (col)\"); } catch (e) { if (/not supported on collections containing durations/.test(e.message)) { /* remodel column type */ } else throw e; }","preventionTips":["Never put duration inside collection type parameters","Use bigint/timestamp for time quantities you intend to query","Lint schema migrations for duration in indexed columns"],"tags":["cassandra","cql","secondary-index","duration"],"backgroundTag":"schema-validation-failed","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}