{"record":{"id":"82403af0f6ceabfe","repo":"apache/cassandra","slug":"cannot-set-default-time-to-live-on-a-table-with-co","errorCode":null,"errorMessage":"Cannot set default_time_to_live on a table with counters","messagePattern":"Cannot set default_time_to_live on a table with counters","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java","lineNumber":692,"sourceCode":"                                 keyspaceName, tableName));\n\n            return next.unbuild().transactionalMigrationFrom(newMigrateFrom).build();\n        }\n\n        @Override\n        public boolean compatibleWith(ClusterMetadata metadata)\n        {\n            return metadata.directory.commonSerializationVersion.isAtLeast(Version.V0);\n        }\n\n        public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table, ClusterMetadata metadata)\n        {\n            attrs.validate();\n\n            TableParams params = attrs.asAlteredTableParams(table.params);\n\n            if (table.isCounter() && params.defaultTimeToLive > 0)\n                throw ire(\"Cannot set default_time_to_live on a table with counters\");\n\n            if (!isEmpty(keyspace.views.forTable(table.id)) && params.gcGraceSeconds == 0)\n            {\n                throw ire(\"Cannot alter gc_grace_seconds of the base table of a \" +\n                          \"materialized view to 0, since this value is used to TTL \" +\n                          \"undelivered updates. Setting gc_grace_seconds too low might \" +\n                          \"cause undelivered updates to expire \" +\n                          \"before being replayed.\");\n            }\n\n            if (keyspace.replicationStrategy.hasTransientReplicas()\n                && params.readRepair != ReadRepairStrategy.NONE)\n            {\n                throw ire(\"read_repair must be set to 'NONE' for transiently replicated keyspaces\");\n            }\n\n            if (!params.compression.isEnabled())\n                Guardrails.uncompressedTablesEnabled.ensureEnabled(state);","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java#L674-L710","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not set default_time_to_live on counter tables (keep it 0)","Handle expiry of counters in the application layer (separate bucket/timestamp rows and periodic cleanup)","Move the counter data into a regular table with TTL if time-based expiry is truly required"],"exampleFix":"// before\nALTER TABLE metrics.hits WITH default_time_to_live = 86400;\n// after\nALTER TABLE metrics.hits WITH comment = 'counters'; // no default_time_to_live","handlingStrategy":"validation","validationCode":"TableMetadata t = Schema.instance.getTableMetadata(ks, table); if (t.isCounter() && attrs.defaultTimeToLive > 0) { /* reject or drop the default_time_to_live setting */ }","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Never include default_time_to_live in generic WITH templates for counter tables","Handle counter expiry at the application level","Lint schema scripts for TTL options applied to counter tables"],"tags":["cassandra","cql","counter-table","ttl","schema"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}