apache/cassandra · error · InvalidRequestException

Non-frozen UDTs with nested non-frozen collections are not s

Error message

Non-frozen UDTs with nested non-frozen collections are not supported for column <name>

What it means

A non-frozen user-defined type (UDT) may not contain fields whose types are themselves multi-cell (non-frozen collections such as list/map/set, or nested non-frozen UDTs). Cassandra requires such nested multi-cell fields to be frozen. The check runs when adding (or altering to add) a column whose declared type is a non-frozen UDT with at least one multi-cell field.

Source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java:375

                return;
            }

            if (type.isCounter() && (table.params.transactionalMode.accordIsEnabled || table.params.transactionalMigrationFrom.migratingFromAccord()))
                throw ire(format(ACCORD_COUNTER_COLUMN_UNSUPPORTED, keyspaceName, tableName, table.params.transactionalMode, table.params.transactionalMigrationFrom));

            if (table.isCompactTable())
                throw ire("Cannot add new column to a COMPACT STORAGE table");

            if (isStatic && table.clusteringColumns().isEmpty())
                throw ire("Static columns are only useful (and thus allowed) if the table has at least one clustering column");

            // check for nested non-frozen UDTs or collections in a non-frozen UDT
            if (type.isUDT() && type.isMultiCell())
            {
                for (AbstractType<?> fieldType : ((UserType) type).fieldTypes())
                {
                    if (fieldType.isMultiCell())
                        throw ire("Non-frozen UDTs with nested non-frozen collections are not supported for column " + column.name);
                }
            }

            ColumnMetadata droppedColumn = table.getDroppedColumn(name.bytes);
            if (null != droppedColumn)
            {
                // After #8099, not safe to re-add columns of incompatible types - until *maybe* deser logic with dropped
                // columns is pushed deeper down the line. The latter would still be problematic in cases of schema races.
                if (!type.isSerializationCompatibleWith(droppedColumn.type))
                {
                    throw ire("Cannot add a column '%s' of type %s, incompatible with previously dropped column '%s' of type %s",
                              name,
                              type.asCQL3Type(),
                              name,
                              droppedColumn.type.asCQL3Type());
                }

                if (droppedColumn.isStatic() != isStatic)

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Freeze the nested collection inside the UDT: ALTER TYPE address ADD tags frozen<list<text>>; or redefine the type with frozen collection fields.
  2. Freeze the whole column instead: ALTER TABLE t ADD col frozen<address> (note frozen values are overwritten as a whole, no partial updates).
  3. Restructure the UDT to hold only primitives or frozen types.
  4. If nesting is genuinely needed, model the nested data as a separate table keyed by the parent partition key.

Example fix

// before
CREATE TYPE address (tags list<text>);
ALTER TABLE users ADD addr address;            -- fails: nested non-frozen list
// after
CREATE TYPE address (tags frozen<list<text>>);
ALTER TABLE users ADD addr address;            -- or: ADD addr frozen<address>
Defensive patterns

Strategy: validation

Validate before calling

// before adding a UDT column, inspect its fields
UserType udt = (UserType) keyspace.types.get(udtName).getType();
if (!udt.isFrozen() && java.util.Arrays.stream(udt.fieldTypes()).anyMatch(AbstractType::isMultiCell))
    throw new IllegalArgumentException("UDT " + udtName + " contains non-frozen collections; freeze fields or freeze the column.");

Type guard

boolean udtAddable(AbstractType<?> t) {
    return !(t.isUDT() && t.isMultiCell()
        && java.util.Arrays.stream(((UserType) t).fieldTypes()).anyMatch(AbstractType::isMultiCell));
}

Try / catch

try {
    session.execute("ALTER TABLE t ADD col " + udtName);
} catch (InvalidQueryException e) {
    if (e.getMessage().contains("nested non-frozen collections")) {
        session.execute("ALTER TABLE t ADD col frozen<" + udtName + ">>");
    } else throw e;
}

Prevention

When it happens

Trigger: ALTER TABLE t ADD col <udt_name> where udt_name resolves to a UserType with isMultiCell()==true (non-frozen) that has any field type which is itself a non-frozen collection or non-frozen nested UDT.

Common situations: Declaring CREATE TYPE address (tags list<text>, phones map<text,text>) and then adding it unfrozen; upgrading schemas from older versions where such nesting was inconsistently handled; ORM/model generators emitting nested collections inside UDTs.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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