apache/cassandra · error · InvalidRequestException

counter type is not supported for PRIMARY KEY column

Error message

counter type is not supported for PRIMARY KEY column '%s'

What it means

Counter columns cannot be part of a materialized view's primary key. Counter values are stored specially (only-increment cells) and cannot act as unique key components, so validation rejects them explicitly.

Solutions

  1. Remove the counter column from the PRIMARY KEY and key the view on regular columns
  2. Maintain the count in a separate non-counter bigint column (updated transactionally by the application) if it must be a key
  3. Query counters from the base table and filter in application code

Example fix

// before
CREATE MATERIALIZED VIEW mv AS SELECT pk, hits FROM counters_base WHERE pk IS NOT NULL AND hits IS NOT NULL PRIMARY KEY (pk, hits);
// after
CREATE MATERIALIZED VIEW mv AS SELECT pk, day, hits FROM counters_base WHERE pk IS NOT NULL AND day IS NOT NULL PRIMARY KEY (pk, day);
Defensive patterns

Strategy: validation

Validate before calling

for (String c : primaryKeyCols) if (baseTable.getColumn(c).type.isCounter()) throw new IllegalArgumentException("Counter column in MV primary key: " + c);

Try / catch

try { session.execute(createMvStmt); } catch (InvalidQueryException e) { if (e.getMessage().contains("counter type is not supported")) { /* remove counter from PRIMARY KEY */ } }

Prevention

When it happens

Trigger: CREATE MATERIALIZED VIEW ... PRIMARY KEY (pk, hits) where hits is a counter column.

Common situations: Trying to index/aggregate by counter values in a view; schema where a counter column was mistaken for a regular bigint.

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

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java:262

            ColumnMetadata column = table.getColumn(name);
            if (null == column || !selectedColumns.contains(name))
                throw ire("Unknown column '%s' referenced in PRIMARY KEY for materialized view '%s'", name, viewName);

            if (!primaryKeyColumns.add(name))
                throw ire("Duplicate column '%s' in PRIMARY KEY clause for materialized view '%s'", name, viewName);

            AbstractType<?> type = column.type;

            if (type.isMultiCell())
            {
                if (type.isCollection())
                    throw ire("Invalid non-frozen collection type '%s' for PRIMARY KEY column '%s'", type, name);
                else
                    throw ire("Invalid non-frozen user-defined type '%s' for PRIMARY KEY column '%s'", type, name);
            }

            if (type.isCounter())
                throw ire("counter type is not supported for PRIMARY KEY column '%s'", name);

            if (type.referencesDuration())
                throw ire("duration type is not supported for PRIMARY KEY column '%s'", name);
        });

        // If we give a clustering order, we must explicitly do so for all aliases and in the order of the PK
        if (!clusteringOrder.isEmpty() && !clusteringColumns.equals(new ArrayList<>(clusteringOrder.keySet())))
            throw ire("Clustering key columns must exactly match columns in CLUSTERING ORDER BY directive");

        /*
         * We need to include all of the primary key columns from the base table in order to make sure that we do not
         * overwrite values in the view. We cannot support "collapsing" the base table into a smaller number of rows in
         * the view because if we need to generate a tombstone, we have no way of knowing which value is currently being
         * used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require
         * that they include all of the columns. We provide them with a list of all of the columns left to include.
         */
        List<ColumnIdentifier> missingPrimaryKeyColumns =
            Lists.newArrayList(filter(transform(table.primaryKeyColumns(), c -> c.name), c -> !primaryKeyColumns.contains(c)));

View on GitHub (pinned to 88fd0f6a0e)