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

  1. 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
  2. If recursion is needed, store child references by primary key (ids) rather than embedding the UDT in itself
  3. 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

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


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