apache/cassandra · error · InvalidRequestException

Cannot set %s on a table with counters

Error message

Cannot set %s on a table with counters

What it means

Counter tables cannot have a default_time_to_live greater than zero, because TTL semantics on counters are restricted. The builder throws this error when a table containing counter columns specifies any table parameter that includes DEFAULT_TIME_TO_LIVE with a positive value.

Source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java:384

            // Static columns only make sense if we have at least one clustering column. Otherwise everything is static anyway
            if (clusteringColumns.isEmpty() && !staticColumns.isEmpty())
                throw ire("Static columns are only useful (and thus allowed) if the table has at least one clustering column");
        }

        /*
         * Counter table validation
         */

        boolean hasCounters = rawColumns.values().stream().anyMatch(c -> c.rawType.isCounter());
        if (hasCounters)
        {
            // We've handled anything that is not a PRIMARY KEY so columns only contains NON-PK columns. So
            // if it's a counter table, make sure we don't have non-counter types
            if (columns.values().stream().anyMatch(t -> !t.type.isCounter()))
                throw ire("Cannot mix counter and non counter columns in the same table");

            if (params.defaultTimeToLive > 0)
                throw ire("Cannot set %s on a table with counters", TableParams.Option.DEFAULT_TIME_TO_LIVE);
        }

        /*
         * Create the builder
         */

        TableMetadata.Builder builder = TableMetadata.builder(keyspaceName, tableName);

        if (attrs.hasProperty(TableAttributes.ID))
            builder.id(attrs.getId());

        builder.isCounter(hasCounters)
               .params(params);

        for (int i = 0; i < partitionKeyColumns.size(); i++)
        {
            ColumnProperties properties = partitionKeyColumnProperties.get(i);
            ColumnIdentifier columnIdentifier = partitionKeyColumns.get(i);

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Remove or set default_time_to_live = 0 for the counter table.
  2. Handle expiry at the application level or move counters to a non-counter table (counters can't be mixed with other columns).

Example fix

// before
CREATE TABLE t (pk int PRIMARY KEY, c counter) WITH default_time_to_live = 86400;
// after
CREATE TABLE t (pk int PRIMARY KEY, c counter);
Defensive patterns

Strategy: validation

Validate before calling

// Counter tables must not set a positive default_time_to_live:
boolean isCounterTable = true;
int defaultTtl = 86400;
if (isCounterTable && defaultTtl > 0) throw new IllegalArgumentException("default_time_to_live not allowed on counter tables");

Try / catch

try { session.execute(ddl); } catch (com.datastax.driver.core.exceptions.InvalidQueryException e) { if (e.getMessage().contains("Cannot set") && e.getMessage().contains("counters")) { /* remove TTL */ } else throw e; }

Prevention

When it happens

Trigger: CREATE TABLE t (pk int PRIMARY KEY, c counter) WITH default_time_to_live = 86400;

Common situations: Reusing a table-options template (with TTL) for a counter table; automated schema generation applying uniform TTL settings.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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