apache/cassandra · error · InvalidRequestException

Cannot set default_time_to_live on a table with counters

Error message

Cannot set default_time_to_live on a table with counters

What it means

Counter tables cannot have a positive default_time_to_live, because counter mutations cannot be TTL'd consistently. In AlterTableStatement.apply (line 692), after computing the altered TableParams, if the table isCounter() and params.defaultTimeToLive > 0 this InvalidRequest is thrown.

Source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java:692

                                 keyspaceName, tableName));

            return next.unbuild().transactionalMigrationFrom(newMigrateFrom).build();
        }

        @Override
        public boolean compatibleWith(ClusterMetadata metadata)
        {
            return metadata.directory.commonSerializationVersion.isAtLeast(Version.V0);
        }

        public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table, ClusterMetadata metadata)
        {
            attrs.validate();

            TableParams params = attrs.asAlteredTableParams(table.params);

            if (table.isCounter() && params.defaultTimeToLive > 0)
                throw ire("Cannot set default_time_to_live on a table with counters");

            if (!isEmpty(keyspace.views.forTable(table.id)) && params.gcGraceSeconds == 0)
            {
                throw ire("Cannot alter gc_grace_seconds of the base table of a " +
                          "materialized view to 0, since this value is used to TTL " +
                          "undelivered updates. Setting gc_grace_seconds too low might " +
                          "cause undelivered updates to expire " +
                          "before being replayed.");
            }

            if (keyspace.replicationStrategy.hasTransientReplicas()
                && params.readRepair != ReadRepairStrategy.NONE)
            {
                throw ire("read_repair must be set to 'NONE' for transiently replicated keyspaces");
            }

            if (!params.compression.isEnabled())
                Guardrails.uncompressedTablesEnabled.ensureEnabled(state);

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Do not set default_time_to_live on counter tables (keep it 0)
  2. Handle expiry of counters in the application layer (separate bucket/timestamp rows and periodic cleanup)
  3. Move the counter data into a regular table with TTL if time-based expiry is truly required

Example fix

// before
ALTER TABLE metrics.hits WITH default_time_to_live = 86400;
// after
ALTER TABLE metrics.hits WITH comment = 'counters'; // no default_time_to_live
Defensive patterns

Strategy: validation

Validate before calling

TableMetadata t = Schema.instance.getTableMetadata(ks, table); if (t.isCounter() && attrs.defaultTimeToLive > 0) { /* reject or drop the default_time_to_live setting */ }

Try / catch

try { session.execute(alterStmt); } catch (InvalidRequestException e) { if (e.getMessage().contains("Cannot set default_time_to_live on a table with counters")) { /* remove TTL from the counter table change */ } else throw e; }

Prevention

When it happens

Trigger: ALTER TABLE counter_table WITH default_time_to_live = 86400; (or any value > 0), either directly or via a default TTL inherited from a copied/parameterized schema script.

Common situations: Applying a generic WITH clause template to all tables including counter tables; assuming TTL works on counters like regular columns; migrating DDL from non-Cassandra setups.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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