apache/cassandra · error · InvalidRequestException

Cannot add field to type : a field with name already exists

Error message

Cannot add field %s to type %s: a field with name %s already exists

What it means

Thrown by ALTER TYPE ... ADD when the type already contains a field with the given name. Cassandra rejects duplicate field names within a user type unless `IF NOT EXISTS` is supplied for the field, in which case the statement is a no-op for that field. The check uses `userType.fieldPosition(fieldName) >= 0` to detect the collision.

Solutions

  1. Add `IF NOT EXISTS` to make the statement idempotent: `ALTER TYPE ks.typ ADD field text IF NOT EXISTS;`
  2. Pick a different field name for the new attribute
  3. Check the current type definition with `DESCRIBE TYPE ks.typ;` before running the migration

Example fix

// before
ALTER TYPE ks.address ADD zip text;
// after
ALTER TYPE ks.address ADD zip text IF NOT EXISTS;
Defensive patterns

Strategy: validation

Validate before calling

Row r = session.execute("SELECT field_names FROM system_schema.types WHERE keyspace_name=? AND type_name=?", ks, typeName).one();
boolean fieldExists = r.getList("field_names", String.class).stream().anyMatch(f -> f.equalsIgnoreCase(newField));
if (fieldExists) throw new IllegalStateException("Field " + newField + " already exists in " + ks + "." + typeName);

Type guard

boolean udtHasField(Session s, String ks, String typeName, String fieldName) {
    Row r = s.execute("SELECT field_names FROM system_schema.types WHERE keyspace_name=? AND type_name=?", ks, typeName).one();
    return r != null && r.getList("field_names", String.class).stream().anyMatch(f -> f.equalsIgnoreCase(fieldName));
}

Try / catch

try { session.execute(alterTypeStmt); }
catch (InvalidQueryException e) {
    if (e.getMessage().startsWith("Cannot add field ") && e.getMessage().contains("already exists")) {
        // treat as already-applied
    } else throw e;
}

Prevention

When it happens

Trigger: Executing `ALTER TYPE ks.typ ADD existing_field type` where a field with that exact name already exists in the UDT and the statement does not use `IF NOT EXISTS`.

Common situations: Idempotent migration scripts re-run against a cluster where the field was already added; typos reusing an existing field name; concurrent migrations adding the same field 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/fccd86d7d2617846. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java:151

        @Override
        public boolean compatibleWith(ClusterMetadata metadata)
        {
            return metadata.directory.commonSerializationVersion.isAtLeast(Version.V0);
        }

        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);

View on GitHub (pinned to 88fd0f6a0e)