apache/cassandra · error · InvalidRequestException

Secondary indexes are not supported on UDTs containing durat

Error message

Secondary indexes are not supported on UDTs containing durations

What it means

Cassandra rejects secondary indexes on user-defined types (UDTs) that contain a `duration` field. Since UDT fields are not individually indexable anyway and duration is unindexable, any UDT referencing duration is rejected. Raised in validateIndexTarget when the column type is a UDT referencing duration.

Source

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

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

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

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Remove the duration field from the UDT or change it to bigint/timestamp.
  2. Create a narrower UDT without duration for the indexed column.
  3. Denormalize the fields you need to query into a dedicated indexed column.

Example fix

// before
CREATE TYPE interval (start_ts timestamp, len duration);
CREATE INDEX ON schedules (span); // span frozen<interval>
// after
CREATE TYPE interval_idx (start_ts timestamp, len_ms bigint);
CREATE INDEX ON schedules (span);
Defensive patterns

Strategy: validation

Validate before calling

if (column.type.isUDT() && column.type.referencesDuration()) throw new Error('unindexable UDT: contains duration field');

Type guard

const isIndexableUdt = (t) => t.isUDT() && !t.referencesDuration();

Try / catch

try { session.execute(ddl); } catch (e) { if (/not supported on UDTs containing durations/.test(e.message)) { /* revise UDT definition */ } else throw e; }

Prevention

When it happens

Trigger: `CREATE INDEX ON t (udt_col)` where udt_col's type definition includes a duration field (e.g. CREATE TYPE interval (start timestamp, len duration)), including frozen and non-frozen UDTs.

Common situations: Indexing a frozen UDT with full() after adding a duration field; shared UDT reused across tables where one table's index DDL no longer validates.

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