apache/cassandra · error · InvalidRequestException

Invalid non-frozen collection type

Error message

Invalid non-frozen collection type '%s' for PRIMARY KEY column '%s'

What it means

Primary key columns of a materialized view must be single-cell values. Non-frozen collections and non-frozen user-defined types are multi-cell and cannot serve as view keys; counter columns are likewise banned.

Solutions

  1. Wrap the collection/UDT in frozen<>, e.g. frozen<set<text>> (requires recreating the base table or migrating data, since types cannot be altered in place)
  2. Choose a scalar column as the view key and add the collection as a non-key projected column
  3. Denormalize collection elements into individual rows/columns in a base table maintained by the application

Example fix

// before
CREATE TABLE base (pk text, tags set<text>, v int, PRIMARY KEY (pk));
CREATE MATERIALIZED VIEW mv AS SELECT pk, tags, v FROM base WHERE pk IS NOT NULL AND tags IS NOT NULL PRIMARY KEY (pk, tags);
// after
CREATE TABLE base2 (pk text, tags frozen<set<text>>, v int, PRIMARY KEY (pk));
CREATE MATERIALIZED VIEW mv AS SELECT pk, tags, v FROM base2 WHERE pk IS NOT NULL AND tags IS NOT NULL PRIMARY KEY (pk, tags);
Defensive patterns

Strategy: validation

Validate before calling

for (String c : primaryKeyCols) { AbstractType<?> t = baseTable.getColumn(c).type; if (t.isMultiCell() && t.isCollection()) throw new IllegalArgumentException("Frozen collection required for key: " + c); }

Type guard

boolean isKeySafe(AbstractType<?> t) { return !t.isMultiCell() || (t instanceof FrozenType); }

Try / catch

try { session.execute(createMvStmt); } catch (InvalidQueryException e) { if (e.getMessage().contains("non-frozen collection")) { /* use frozen<> or pick another key column */ } }

Prevention

When it happens

Trigger: CREATE MATERIALIZED VIEW whose PRIMARY KEY includes a non-frozen list/map/set or non-frozen UDT, e.g. PRIMARY KEY (pk, tags) where tags is a set<text>.

Common situations: Using a collection to group rows into a view key; schema drift where a column became a collection later; forgetting the frozen<> keyword when the table was created.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

            throw ire("Must provide at least one partition key column for materialized view '%s'", viewName);

        HashSet<ColumnIdentifier> primaryKeyColumns = new HashSet<>();

        concat(partitionKeyColumns, clusteringColumns).forEach(name ->
        {
            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

View on GitHub (pinned to 88fd0f6a0e)