apache/cassandra · error · InvalidRequestException
Cannot add new field %s of type %s to user type %s as it wou
Error message
Cannot add new field %s of type %s to user type %s as it would create a circular reference
What it means
Cassandra rejects an ALTER TYPE ... ADD whose new field's type resolves to a UDT that references the very type being altered, which would make the type definition circular (a cycle the type system cannot serialize or validate). The check is performed in AddField.apply after resolving the field type against the keyspace's types. Circular UDT references are not representable in Cassandra's schema, so the operation fails fast with an InvalidRequest.
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java:157
UserType apply(KeyspaceMetadata keyspace, UserType userType)
{
if (type.isCounter())
throw ire("A user type cannot contain counters");
if (type.isUDT() && !type.isFrozen())
throw ire("A user type cannot contain non-frozen UDTs");
if (userType.fieldPosition(fieldName) >= 0)
{
if (!ifFieldNotExists)
throw ire("Cannot add field %s to type %s: a field with name %s already exists", fieldName, userType.getCqlTypeName(), fieldName);
return userType;
}
AbstractType<?> fieldType = type.prepare(keyspaceName, keyspace.types).getType();
if (fieldType.referencesUserType(userType.name))
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());
Collection<TableMetadata> tablesWithTypeInPartitionKey = findTablesReferencingTypeInPartitionKey(keyspace, userType);
if (!tablesWithTypeInPartitionKey.isEmpty())
{
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",
fieldName, type, userType.getCqlTypeName(),
String.join(", ", transform(tablesWithTypeInPartitionKey, TableMetadata::toString)));
}
Guardrails.fieldsPerUDT.guard(userType.size() + 1, userType.getNameAsString(), false, state);
type.validate(state, "Field " + fieldName);
List<FieldIdentifier> fieldNames = new ArrayList<>(userType.fieldNames()); fieldNames.add(fieldName);
List<AbstractType<?>> fieldTypes = new ArrayList<>(userType.fieldTypes()); fieldTypes.add(fieldType);
return new UserType(keyspaceName, userType.name, fieldNames, fieldTypes, true);
}
View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Remove the self/circular reference: declare the new field with a non-recursive type, or model recursion via a separate table instead of a nested UDT
- If recursion is needed, store child references by primary key (ids) rather than embedding the UDT in itself
- Reorder the cycle: split into two UDTs so neither contains itself, only the other (still disallowed if truly circular — break the cycle entirely)
Example fix
// before ALTER TYPE myks.node ADD parent myks.node; // circular // after ALTER TYPE myks.node ADD parent_uuid uuid; // store reference by id
Defensive patterns
Strategy: validation
Validate before calling
// Before: ALTER TYPE t ADD f t;
boolean createsCycle = newFieldTypeResolved.referencesUserType(userType.name);
if (createsCycle) throw new IllegalArgumentException("Field type would create a circular UDT reference"); Try / catch
try { session.execute("ALTER TYPE myks.t ADD f myks.t"); }
catch (InvalidQueryException e) {
if (e.getMessage().contains("circular reference")) { /* redesign: break the cycle */ }
else throw e;
} Prevention
- Never declare a UDT field whose type is (or contains) the UDT itself
- Model recursive data with id references plus a lookup table instead of nested UDTs
- Dry-run schema changes in a test keyspace mirroring the types involved
When it happens
Trigger: Running `ALTER TYPE myks.address ADD owner myks.address` or any ADD whose field type is (directly or nested within a frozen/tuple/collection type) the same UDT being altered, e.g. adding a field of a type that transitively contains the altered type.
Common situations: Modeling recursive structures (tree nodes, linked lists) with UDTs; copy-pasting CQL that used another type name; schema migrations where a nested UDT was renamed to the outer type's name.
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
- Cannot add new field %s of type %s to user type %s as the ty
- category %s not found in %s
- REVOKE operation is not supported by AllowAllAuthorizer
- 'Get CIDR groups for IP' operation not supported by %s
- ACCESS TO DATACENTERS operations not supported by AllowAllNe
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/ae07ce0ee7a1be44.
Report an issue: GitHub.