{"record":{"id":"54c43743d2f3f48d","repo":"apache/cassandra","slug":"non-frozen-udts-with-nested-non-frozen-collections","errorCode":null,"errorMessage":"Non-frozen UDTs with nested non-frozen collections are not supported for column <name>","messagePattern":"Non-frozen UDTs with nested non-frozen collections are not supported for column <name>","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java","lineNumber":375,"sourceCode":"                return;\n            }\n\n            if (type.isCounter() && (table.params.transactionalMode.accordIsEnabled || table.params.transactionalMigrationFrom.migratingFromAccord()))\n                throw ire(format(ACCORD_COUNTER_COLUMN_UNSUPPORTED, keyspaceName, tableName, table.params.transactionalMode, table.params.transactionalMigrationFrom));\n\n            if (table.isCompactTable())\n                throw ire(\"Cannot add new column to a COMPACT STORAGE table\");\n\n            if (isStatic && table.clusteringColumns().isEmpty())\n                throw ire(\"Static columns are only useful (and thus allowed) if the table has at least one clustering column\");\n\n            // check for nested non-frozen UDTs or collections in a non-frozen UDT\n            if (type.isUDT() && type.isMultiCell())\n            {\n                for (AbstractType<?> fieldType : ((UserType) type).fieldTypes())\n                {\n                    if (fieldType.isMultiCell())\n                        throw ire(\"Non-frozen UDTs with nested non-frozen collections are not supported for column \" + column.name);\n                }\n            }\n\n            ColumnMetadata droppedColumn = table.getDroppedColumn(name.bytes);\n            if (null != droppedColumn)\n            {\n                // After #8099, not safe to re-add columns of incompatible types - until *maybe* deser logic with dropped\n                // columns is pushed deeper down the line. The latter would still be problematic in cases of schema races.\n                if (!type.isSerializationCompatibleWith(droppedColumn.type))\n                {\n                    throw ire(\"Cannot add a column '%s' of type %s, incompatible with previously dropped column '%s' of type %s\",\n                              name,\n                              type.asCQL3Type(),\n                              name,\n                              droppedColumn.type.asCQL3Type());\n                }\n\n                if (droppedColumn.isStatic() != isStatic)","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java#L357-L393","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Freeze the nested collection inside the UDT: ALTER TYPE address ADD tags frozen<list<text>>; or redefine the type with frozen collection fields.","Freeze the whole column instead: ALTER TABLE t ADD col frozen<address> (note frozen values are overwritten as a whole, no partial updates).","Restructure the UDT to hold only primitives or frozen types.","If nesting is genuinely needed, model the nested data as a separate table keyed by the parent partition key."],"exampleFix":"// before\nCREATE TYPE address (tags list<text>);\nALTER TABLE users ADD addr address;            -- fails: nested non-frozen list\n// after\nCREATE TYPE address (tags frozen<list<text>>);\nALTER TABLE users ADD addr address;            -- or: ADD addr frozen<address>","handlingStrategy":"validation","validationCode":"// before adding a UDT column, inspect its fields\nUserType udt = (UserType) keyspace.types.get(udtName).getType();\nif (!udt.isFrozen() && java.util.Arrays.stream(udt.fieldTypes()).anyMatch(AbstractType::isMultiCell))\n    throw new IllegalArgumentException(\"UDT \" + udtName + \" contains non-frozen collections; freeze fields or freeze the column.\");","typeGuard":"boolean udtAddable(AbstractType<?> t) {\n    return !(t.isUDT() && t.isMultiCell()\n        && java.util.Arrays.stream(((UserType) t).fieldTypes()).anyMatch(AbstractType::isMultiCell));\n}","tryCatchPattern":"try {\n    session.execute(\"ALTER TABLE t ADD col \" + udtName);\n} catch (InvalidQueryException e) {\n    if (e.getMessage().contains(\"nested non-frozen collections\")) {\n        session.execute(\"ALTER TABLE t ADD col frozen<\" + udtName + \">>\");\n    } else throw e;\n}","preventionTips":["Define UDT fields as frozen<list<...>>/frozen<map<...>> whenever nesting is required.","Audit UDT definitions with system_schema.types before referencing them in ALTER statements.","Remember frozen columns forbid partial field updates — design accordingly before freezing.","Keep UDTs flat (primitive fields only) when non-frozen usage is intended."],"tags":["cql","udt","schema","collections","frozen"],"backgroundTag":"schema-validation-failed","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"}