{"record":{"id":"034e69d0433e9b78","repo":"apache/cassandra","slug":"invalid-non-frozen-user-defined-type-s-for-prim","errorCode":null,"errorMessage":"Invalid non-frozen user-defined type '%s' for PRIMARY KEY column '%s'","messagePattern":"Invalid non-frozen user-defined type '(.+?)' for PRIMARY KEY column '(.+?)'","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java","lineNumber":258,"sourceCode":"        HashSet<ColumnIdentifier> primaryKeyColumns = new HashSet<>();\n\n        concat(partitionKeyColumns, clusteringColumns).forEach(name ->\n        {\n            ColumnMetadata column = table.getColumn(name);\n            if (null == column || !selectedColumns.contains(name))\n                throw ire(\"Unknown column '%s' referenced in PRIMARY KEY for materialized view '%s'\", name, viewName);\n\n            if (!primaryKeyColumns.add(name))\n                throw ire(\"Duplicate column '%s' in PRIMARY KEY clause for materialized view '%s'\", name, viewName);\n\n            AbstractType<?> type = column.type;\n\n            if (type.isMultiCell())\n            {\n                if (type.isCollection())\n                    throw ire(\"Invalid non-frozen collection type '%s' for PRIMARY KEY column '%s'\", type, name);\n                else\n                    throw ire(\"Invalid non-frozen user-defined type '%s' for PRIMARY KEY column '%s'\", type, name);\n            }\n\n            if (type.isCounter())\n                throw ire(\"counter type is not supported for PRIMARY KEY column '%s'\", name);\n\n            if (type.referencesDuration())\n                throw ire(\"duration type is not supported for PRIMARY KEY column '%s'\", name);\n        });\n\n        // If we give a clustering order, we must explicitly do so for all aliases and in the order of the PK\n        if (!clusteringOrder.isEmpty() && !clusteringColumns.equals(new ArrayList<>(clusteringOrder.keySet())))\n            throw ire(\"Clustering key columns must exactly match columns in CLUSTERING ORDER BY directive\");\n\n        /*\n         * We need to include all of the primary key columns from the base table in order to make sure that we do not\n         * overwrite values in the view. We cannot support \"collapsing\" the base table into a smaller number of rows in\n         * the view because if we need to generate a tombstone, we have no way of knowing which value is currently being\n         * used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java#L240-L276","documentation":"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.","triggerScenarios":"CREATE MATERIALIZED VIEW whose PRIMARY KEY includes a non-frozen UDT column, e.g. address (a UDT declared without frozen<>).","commonSituations":"UDTs created un-frozen for schema flexibility, then used as keys in denormalized views; forgetting that only frozen UDTs may appear in keys.","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"],"exampleFix":"// before\nCREATE TABLE base (pk text, addr address, v int, PRIMARY KEY (pk)); -- addr is non-frozen UDT\nCREATE MATERIALIZED VIEW mv AS SELECT pk, addr, v FROM base WHERE pk IS NOT NULL AND addr IS NOT NULL PRIMARY KEY (pk, addr);\n// after\nCREATE TABLE base2 (pk text, addr frozen<address>, v int, PRIMARY KEY (pk));\nCREATE MATERIALIZED VIEW mv AS SELECT pk, addr, v FROM base2 WHERE pk IS NOT NULL AND addr IS NOT NULL PRIMARY KEY (pk, addr);","handlingStrategy":"validation","validationCode":"for (String c : primaryKeyCols) { AbstractType<?> t = baseTable.getColumn(c).type; if (t.isMultiCell() && !t.isCollection()) throw new IllegalArgumentException(\"Frozen UDT required for key: \" + c); }","typeGuard":"boolean isUdtKeySafe(AbstractType<?> t) { return !t.isMultiCell() || (t instanceof FrozenType); }","tryCatchPattern":"try { session.execute(createMvStmt); } catch (InvalidQueryException e) { if (e.getMessage().contains(\"non-frozen user-defined type\")) { /* wrap UDT in frozen<> */ } }","preventionTips":["Declare UDTs used in keys as frozen<udt> from the start","Keep non-frozen UDTs out of primary keys"],"tags":["cql","materialized-view","primary-key","frozen-udt"],"backgroundTag":"invalid-argument-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}