apache/cassandra · error · InvalidRequestException
Cannot drop non-frozen column %s of user type %s
Error message
Cannot drop non-frozen column %s of user type %s
What it means
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.
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java:510
private void dropColumn(KeyspaceMetadata keyspace, TableMetadata table, ColumnIdentifier column, boolean ifExists, TableMetadata.Builder builder)
{
ColumnMetadata currentColumn = table.getColumn(column);
if (null == currentColumn) {
if (!ifExists)
throw ire("Column %s was not found in table '%s'", column, table);
return;
}
if (currentColumn.isPrimaryKeyColumn())
throw ire("Cannot drop PRIMARY KEY column %s", column);
/*
* Cannot allow dropping top-level columns of user defined types that aren't frozen because we cannot convert
* the type into an equivalent tuple: we only support frozen tuples currently. And as such we cannot persist
* the correct type in system_schema.dropped_columns.
*/
if (currentColumn.type.isUDT() && currentColumn.type.isMultiCell())
throw ire("Cannot drop non-frozen column %s of user type %s", column, currentColumn.type.asCQL3Type());
if (!table.indexes.isEmpty())
AlterTableStatement.validateIndexesForColumnModification(table, column, false);
if (!isEmpty(keyspace.views.forTable(table.id)))
throw ire("Cannot drop column %s on base table %s with materialized views", currentColumn, table.name);
builder.removeRegularOrStaticColumn(column);
builder.recordColumnDrop(currentColumn, getTimestamp());
}
/**
* @return timestamp from query, otherwise return current time in micros
*/
private long getTimestamp()
{
// Prior to Metadata serialization V5, the execution timestamp was not included in AlterSchema
// serializations. Instead, the current time (from ClientState::getTimestamp) was used, makingView on GitHub (pinned to 88fd0f6a0e)
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.
Example fix
// before ALTER TABLE users DROP address; -- address is a non-frozen UDT: fails // after ALTER TABLE users ADD address_f frozen<address>; UPDATE users SET address_f = address; -- per row / via migration ALTER TABLE users DROP address;
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check the column type for a non-frozen UDT
ResultSet rs = session.execute(
"SELECT type FROM system_schema.columns WHERE keyspace_name=? AND table_name=? AND column_name=?",
ks, table, column);
String type = rs.one().getString("type");
boolean blocked = type.startsWith("<") && !type.startsWith("frozen"); // non-frozen UDT/collection types are encoded with '<' Try / catch
try {
session.execute("ALTER TABLE ks.tbl DROP " + column);
} catch (com.datastax.oss.driver.api.core.servererrors.InvalidQueryException e) {
if (e.getMessage().contains("non-frozen column")) {
// switch to freeze-copy-drop migration
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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()`.
Common situations: 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.
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
- Non-frozen UDTs are not allowed inside collections:
- Statement on keyspace %s cannot refer to a user type in keys
- Invalid field deletion operation for non-UDT column %s
- Unknown field '%s' in value of user defined type %s
- Static columns are only useful (and thus allowed) if the tab
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/272ea8e9a689219b.
Report an issue: GitHub.