{"record":{"id":"6067b70bf1ae44e9","repo":"apache/cassandra","slug":"cannot-add-new-field-s-of-type-s-to-user-type-s-6067b7","errorCode":null,"errorMessage":"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","messagePattern":"Cannot add new field (.+?) of type (.+?) to user type (.+?) as the type is being used in partition key by the following tables: (.+?)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java","lineNumber":162,"sourceCode":"\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)));\n            }\n\n            Guardrails.fieldsPerUDT.guard(userType.size() + 1, userType.getNameAsString(), false, state);\n            type.validate(state, \"Field \" + fieldName);\n\n            List<FieldIdentifier> fieldNames = new ArrayList<>(userType.fieldNames()); fieldNames.add(fieldName);\n            List<AbstractType<?>> fieldTypes = new ArrayList<>(userType.fieldTypes()); fieldTypes.add(fieldType);\n\n            return new UserType(keyspaceName, userType.name, fieldNames, fieldTypes, true);\n        }\n\n        private static Collection<TableMetadata> findTablesReferencingTypeInPartitionKey(KeyspaceMetadata keyspace, UserType userType)\n        {\n            Collection<TableMetadata> tables = new ArrayList<>();\n            filter(keyspace.tablesAndViews(),\n                   table -> any(table.partitionKeyColumns(), column -> column.type.referencesUserType(userType.name)))","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java#L144-L180","documentation":"Cassandra refuses to add a field to a UDT that is used (unfrozen) in any table's partition key, because changing the type would change partition key serialization and make existing rows unreachable. AddField.apply enumerates tables and views whose partition key columns reference the UDT and rejects the ALTER with the list of offending tables.","triggerScenarios":"`ALTER TYPE myks.addr ADD zip text` where some table was created with `PRIMARY KEY ((addr), ...)` containing the addr UDT unfrozen in the partition key.","commonSituations":"UDTs accidentally placed in partition keys early in design, then needing evolution later; multi-DC migrations where schema drift re-introduced the UDT into a partition key.","solutions":["Drop and recreate dependent tables without the UDT in the partition key, add the field, then recreate/repopulate the data","Create a new table with a different design (e.g. freeze the UDT or move it to a regular column), migrate data, drop the old table","Use a new UDT version (e.g. addr_v2) for the new schema and migrate tables away from the old type"],"exampleFix":"// before\nCREATE TABLE events (k frozen<addr>, ... PRIMARY KEY ((k), ts)); -- still blocks add\n-- after: move UDT out of partition key\nCREATE TABLE events (id uuid, k addr, ..., PRIMARY KEY (id, ts));","handlingStrategy":"validation","validationCode":"// Before altering a UDT, check partition-key usage:\nResultSet rs = session.execute(\"SELECT table_name, column_name FROM system_schema.columns \" +\n    \"WHERE keyspace_name='myks' AND kind='partition_key'\");\n// verify none of the partition key column types reference the UDT before ALTER TYPE ... ADD","typeGuard":null,"tryCatchPattern":"try { session.execute(alterTypeCql); }\ncatch (InvalidQueryException e) {\n    if (e.getMessage().contains(\"used in partition key\")) { /* migrate tables listed in the message first */ }\n    else throw e;\n}","preventionTips":["Avoid unfrozen UDTs in partition keys; keep partition keys on primitives (uuid, text, int)","Query system_schema.columns for partition_key columns referencing the UDT before any ALTER TYPE","Plan UDT evolution before creating tables that use the type in keys"],"tags":["cassandra","cql","udt","partition-key","schema"],"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"}