{"record":{"id":"272ea8e9a689219b","repo":"apache/cassandra","slug":"cannot-drop-non-frozen-column-s-of-user-type-s","errorCode":null,"errorMessage":"Cannot drop non-frozen column %s of user type %s","messagePattern":"Cannot drop non-frozen column (.+?) of user type (.+?)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java","lineNumber":510,"sourceCode":"        private void dropColumn(KeyspaceMetadata keyspace, TableMetadata table, ColumnIdentifier column, boolean ifExists, TableMetadata.Builder builder)\n        {\n            ColumnMetadata currentColumn = table.getColumn(column);\n            if (null == currentColumn) {\n                if (!ifExists)\n                    throw ire(\"Column %s was not found in table '%s'\", column, table);\n                return;\n            }\n\n            if (currentColumn.isPrimaryKeyColumn())\n                throw ire(\"Cannot drop PRIMARY KEY column %s\", column);\n\n            /*\n             * Cannot allow dropping top-level columns of user defined types that aren't frozen because we cannot convert\n             * the type into an equivalent tuple: we only support frozen tuples currently. And as such we cannot persist\n             * the correct type in system_schema.dropped_columns.\n             */\n            if (currentColumn.type.isUDT() && currentColumn.type.isMultiCell())\n                throw ire(\"Cannot drop non-frozen column %s of user type %s\", column, currentColumn.type.asCQL3Type());\n\n            if (!table.indexes.isEmpty())\n                AlterTableStatement.validateIndexesForColumnModification(table, column, false);\n\n            if (!isEmpty(keyspace.views.forTable(table.id)))\n                throw ire(\"Cannot drop column %s on base table %s with materialized views\", currentColumn, table.name);\n\n            builder.removeRegularOrStaticColumn(column);\n            builder.recordColumnDrop(currentColumn, getTimestamp());\n        }\n\n        /**\n         * @return timestamp from query, otherwise return current time in micros\n         */\n        private long getTimestamp()\n        {\n            // Prior to Metadata serialization V5, the execution timestamp was not included in AlterSchema\n            // serializations. Instead, the current time (from ClientState::getTimestamp) was used, making","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java#L492-L528","documentation":"This InvalidRequestException is thrown when dropping a column whose type is a non-frozen (multi-cell) user-defined type (UDT). Non-frozen UDT columns cannot be converted into the equivalent tuple form needed to record the drop in system_schema.dropped_columns, because Cassandra only supports frozen tuples for persisted dropped-column types.","triggerScenarios":"Executing `ALTER TABLE ks.tbl DROP udt_col` where udt_col was declared as a plain (non-frozen) UDT, e.g. `address address` rather than `FROZEN<address>`. The check is `currentColumn.type.isUDT() && currentColumn.type.isMultiCell()`.","commonSituations":"Tables created with unfrozen UDT columns (common in older schemas where non-frozen UDTs were the default for collections of UDTs or top-level UDT columns); migrations that drop legacy UDT columns; developers unaware of the frozen vs non-frozen distinction.","solutions":["Freeze the UDT column first if the data allows: create a new column of type FROZEN<udt>, copy the data, then drop the old column (note the new column cannot reuse the old name).","Keep the column but stop using it in application code instead of dropping it.","Recreate the table with frozen or non-UDT types and migrate data if the column must not exist.","Check the column type via DESCRIBE TABLE before writing the drop; look for `frozen<...>` in the type."],"exampleFix":"// before\nALTER TABLE users DROP address; -- address is a non-frozen UDT: fails\n// after\nALTER TABLE users ADD address_f frozen<address>;\nUPDATE users SET address_f = address; -- per row / via migration\nALTER TABLE users DROP address;","handlingStrategy":"validation","validationCode":"// Pre-check the column type for a non-frozen UDT\nResultSet rs = session.execute(\n  \"SELECT type FROM system_schema.columns WHERE keyspace_name=? AND table_name=? AND column_name=?\",\n  ks, table, column);\nString type = rs.one().getString(\"type\");\nboolean blocked = type.startsWith(\"<\") && !type.startsWith(\"frozen\"); // non-frozen UDT/collection types are encoded with '<'","typeGuard":null,"tryCatchPattern":"try {\n    session.execute(\"ALTER TABLE ks.tbl DROP \" + column);\n} catch (com.datastax.oss.driver.api.core.servererrors.InvalidQueryException e) {\n    if (e.getMessage().contains(\"non-frozen column\")) {\n        // switch to freeze-copy-drop migration\n    } else throw e;\n}","preventionTips":["Prefer FROZEN<udt> when declaring UDT columns that may later be dropped.","Inspect column types via DESCRIBE TABLE or system_schema.columns before drops.","Document frozen/non-frozen conventions in schema design guidelines.","Test schema migrations against a staging keyspace with production-like types."],"tags":["cql","schema","udt","alter-table"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}