{"record":{"id":"b25959550c4a2227","repo":"apache/cassandra","slug":"a-user-type-cannot-contain-non-frozen-udts","errorCode":null,"errorMessage":"A user type cannot contain non-frozen UDTs","messagePattern":"A user type cannot contain non-frozen UDTs","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java","lineNumber":146,"sourceCode":"            super.validate(state);\n\n            // save the query state to use it for guardrails validation in #apply\n            this.state = state;\n        }\n\n        @Override\n        public boolean compatibleWith(ClusterMetadata metadata)\n        {\n            return metadata.directory.commonSerializationVersion.isAtLeast(Version.V0);\n        }\n\n        UserType apply(KeyspaceMetadata keyspace, UserType userType)\n        {\n            if (type.isCounter())\n                throw ire(\"A user type cannot contain counters\");\n\n            if (type.isUDT() && !type.isFrozen())\n                throw ire(\"A user type cannot contain non-frozen UDTs\");\n\n            if (userType.fieldPosition(fieldName) >= 0)\n            {\n                if (!ifFieldNotExists)\n                    throw ire(\"Cannot add field %s to type %s: a field with name %s already exists\", fieldName, userType.getCqlTypeName(), fieldName);\n                return userType;\n            }\n\n            AbstractType<?> fieldType = type.prepare(keyspaceName, keyspace.types).getType();\n            if (fieldType.referencesUserType(userType.name))\n                throw ire(\"Cannot add new field %s of type %s to user type %s as it would create a circular reference\", fieldName, type, userType.getCqlTypeName());\n\n            Collection<TableMetadata> tablesWithTypeInPartitionKey = findTablesReferencingTypeInPartitionKey(keyspace, userType);\n            if (!tablesWithTypeInPartitionKey.isEmpty())\n            {\n                throw ire(\"Cannot add new field %s of type %s to user type %s as the type is being used in partition key by the following tables: %s\",\n                          fieldName, type, userType.getCqlTypeName(),\n                          String.join(\", \", transform(tablesWithTypeInPartitionKey, TableMetadata::toString)));","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java#L128-L164","documentation":"Thrown when a field being added to or changed in a user-defined type is itself a non-frozen UDT. Cassandra requires nested UDTs inside other UDTs to be frozen, since non-frozen UDTs cannot be used in that nested position. The check runs on the prepared field type before applying the alteration.","triggerScenarios":"Executing `ALTER TYPE ks.typ ADD field ks.other_type` where `ks.other_type` is a non-frozen user type (declared without the FROZEN keyword), or altering a field to such a type.","commonSituations":"Forgetting FROZEN when referencing another UDT; migrations copied from table definitions where non-frozen UDTs are allowed in some positions; older schemas created before frozen enforcement.","solutions":["Wrap the nested type in FROZEN: `ALTER TYPE ks.typ ADD field frozen<ks.other_type>;`","If mutability of the nested UDT is required, store it in a separate table instead of nesting","Freeze an entire tuple-like structure if multiple nested fields are needed"],"exampleFix":"// before\nALTER TYPE ks.person ADD address ks.address;\n// after\nALTER TYPE ks.person ADD address frozen<ks.address>;","handlingStrategy":"validation","validationCode":"String t = fieldType.trim().toLowerCase();\nif (!t.startsWith(\"frozen<\") && isKnownUserType(keyspace, t))\n    throw new IllegalArgumentException(\"Nested UDT \" + t + \" must be frozen\");","typeGuard":"boolean isNonFrozenUdt(String cqlFieldType) {\n    String t = cqlFieldType.trim().toLowerCase();\n    return !t.startsWith(\"frozen<\") && t.matches(\"[a-z0-9_]+[.<].*\");\n}","tryCatchPattern":"try { session.execute(alterTypeStmt); }\ncatch (InvalidQueryException e) {\n    if (\"A user type cannot contain non-frozen UDTs\".equals(e.getMessage())) {\n        // retry with frozen<...> wrapper\n    } else throw e;\n}","preventionTips":["Always wrap nested UDTs in frozen<...> in ALTER/CREATE TYPE statements","Add a lint step to schema migrations that flags non-frozen UDT references","Remember tables are the only place non-frozen UDT collections are allowed"],"tags":["cql","udt","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-17T15:17:12.973Z"}