apache/cassandra · error · InvalidRequestException

Secondary indexes are not supported on duration columns

Error message

Secondary indexes are not supported on duration columns

What it means

Cassandra does not allow secondary indexes on columns of type `duration` at all. Duration lacks the comparison semantics index backends rely on for equality/range matching. This is the fallback branch of the duration check in validateIndexTarget after collection/tuple/UDT cases.

Source

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

        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);

        if (target.type == Type.FULL && isNonSAIIndex && (!baseType.isCollection() || column.type.isMultiCell()))
            throw ire(FULL_ON_FROZEN_COLLECTIONS);

        if (!baseType.isCollection() && target.type != Type.SIMPLE)

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Change the column type to bigint (store milliseconds) or timestamp and index that.
  2. Filter by duration in application code or via ALLOW FILTERING on a scan (with care).
  3. Denormalize into buckets (e.g. duration_class text) that can be indexed.

Example fix

// before
CREATE INDEX ON jobs (elapsed); // elapsed duration
// after
ALTER TABLE jobs ADD elapsed_ms bigint;
CREATE INDEX ON jobs (elapsed_ms);
Defensive patterns

Strategy: validation

Validate before calling

if (column.type.toString() === 'duration') throw new Error('duration columns cannot be secondary-indexed');

Type guard

const isIndexableScalar = (t) => !t.referencesDuration();

Try / catch

try { session.execute(ddl); } catch (e) { if (/not supported on duration columns/.test(e.message)) { /* switch to bigint/timestamp */ } else throw e; }

Prevention

When it happens

Trigger: `CREATE INDEX ON t (dur_col)` where dur_col is a plain duration column (regular, static, or even key columns), via any index kind including custom/SAI.

Common situations: Tracking SLA/elapsed-time with duration then trying to find rows by duration range; writing generic schema tooling that blindly indexes all columns.

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/c9db9f29a19ac019. Report an issue: GitHub.