apache/cassandra · error · InvalidRequestException
Invalid non-frozen user-defined type
Error message
Invalid non-frozen user-defined type '%s' for PRIMARY KEY column '%s'
What it means
Like collections, non-frozen user-defined types are multi-cell and cannot be used as a materialized view primary key column. The validation branches on type.isMultiCell() and rejects non-collection multi-cell types with this message.
Solutions
- Re-create the base table column as frozen<udt_type> (with data migration) and then build the view
- Use a scalar column from within the UDT instead: store it as a regular column and key the view on it
- Keep the UDT out of the view's primary key and project it as a non-key column
Example fix
// before CREATE TABLE base (pk text, addr address, v int, PRIMARY KEY (pk)); -- addr is non-frozen UDT CREATE MATERIALIZED VIEW mv AS SELECT pk, addr, v FROM base WHERE pk IS NOT NULL AND addr IS NOT NULL PRIMARY KEY (pk, addr); // after CREATE TABLE base2 (pk text, addr frozen<address>, v int, PRIMARY KEY (pk)); CREATE MATERIALIZED VIEW mv AS SELECT pk, addr, v FROM base2 WHERE pk IS NOT NULL AND addr IS NOT NULL PRIMARY KEY (pk, addr);
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 UDT required for key: " + c); } Type guard
boolean isUdtKeySafe(AbstractType<?> t) { return !t.isMultiCell() || (t instanceof FrozenType); } Try / catch
try { session.execute(createMvStmt); } catch (InvalidQueryException e) { if (e.getMessage().contains("non-frozen user-defined type")) { /* wrap UDT in frozen<> */ } } Prevention
- Declare UDTs used in keys as frozen<udt> from the start
- Keep non-frozen UDTs out of primary keys
When it happens
Trigger: CREATE MATERIALIZED VIEW whose PRIMARY KEY includes a non-frozen UDT column, e.g. address (a UDT declared without frozen<>).
Common situations: UDTs created un-frozen for schema flexibility, then used as keys in denormalized views; forgetting that only frozen UDTs may appear in keys.
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
- Cannot include more than one non-primary key column in…
- counter type is not supported for PRIMARY KEY column
- Duplicate column ' ' in PRIMARY KEY clause for materialized…
- Invalid non-frozen collection type
- No PRIMARY KEY specifed for view
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/034e69d0433e9b78.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java:258
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
* 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 requireView on GitHub (pinned to 88fd0f6a0e)