apache/cassandra · error · InvalidRequestException

Secondary indexes are not supported on tuples containing dur

Error message

Secondary indexes are not supported on tuples containing durations

What it means

Cassandra rejects secondary indexes on tuple columns that contain a `duration` component. Duration cannot participate in index equality/range semantics, so any tuple referencing duration is unindexable. Raised in validateIndexTarget when the column type is a tuple referencing duration.

Source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java:271

        if (null == column)
            throw ire(COLUMN_DOES_NOT_EXIST, target.column);

        AbstractType<?> baseType = column.type.unwrap();

        boolean isNonSAIIndex = !isSAIIndex(attrs);

        // TODO: this check needs to be removed with CASSANDRA-20235
        if ((kind == IndexMetadata.Kind.CUSTOM))
            validateCustomIndexColumnName(target.column.toString());

        if (column.type.referencesDuration())
        {
            if (column.type.isCollection())
                throw ire(COLLECTIONS_WITH_DURATIONS_NOT_SUPPORTED);

            if (column.type.isTuple())
                throw ire(TUPLES_WITH_DURATIONS_NOT_SUPPORTED);

            if (column.type.isUDT())
                throw  ire(UDTS_WITH_DURATIONS_NOT_SUPPORTED);

            throw ire(DURATIONS_NOT_SUPPORTED);
        }

        if (table.isCompactTable())
        {
            TableMetadata.CompactTableMetadata compactTable = (TableMetadata.CompactTableMetadata) table;
            if (column.isPrimaryKeyColumn())
                throw new InvalidRequestException(PRIMARY_KEY_IN_COMPACT_STORAGE);
            if (compactTable.compactValueColumn.equals(column))
                throw new InvalidRequestException(COMPACT_COLUMN_IN_COMPACT_STORAGE);
        }

        if (column.isPartitionKey() && table.partitionKeyColumns().size() == 1)
            throw ire(ONLY_PARTITION_KEY, column);

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Replace the duration component with an indexable numeric type (bigint milliseconds) or timestamp.
  2. Split the tuple into separate columns and index only the non-duration ones.
  3. Use a materialized view / denormalized table keyed by the values you need to query.

Example fix

// before
CREATE INDEX ON measurements (window); // window frozen<tuple<timestamp, duration>>
// after
ALTER TABLE measurements ADD window_ms frozen<tuple<timestamp, bigint>>;
CREATE INDEX ON measurements (window_ms);
Defensive patterns

Strategy: validation

Validate before calling

if (column.type.isTuple() && column.type.referencesDuration()) throw new Error('unindexable tuple: contains duration');

Type guard

const isIndexableTuple = (t) => t.isTuple() && !t.referencesDuration();

Try / catch

try { session.execute(ddl); } catch (e) { if (/not supported on tuples containing durations/.test(e.message)) { /* replace duration component */ } else throw e; }

Prevention

When it happens

Trigger: `CREATE INDEX ON t (tuple_col)` where tuple_col is e.g. frozen<tuple<int, duration>> or tuple<duration, text>, via CREATE INDEX / CREATE CUSTOM INDEX.

Common situations: Modeling (offset, duration) pairs in a tuple for range queries then adding an index; upgrading a schema that swapped a timestamp component for duration.

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


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