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
- Add `IF NOT EXISTS` to make the statement idempotent: `ALTER TYPE ks.typ ADD field text IF NOT EXISTS;`
- Pick a different field name for the new attribute
- 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
- Use IF NOT EXISTS on ADD field for idempotent migrations
- DESCRIBE TYPE (or system_schema.types) before adding fields
- Make migration scripts track applied DDL so they do not re-run
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
- A user type cannot contain counters
- A user type cannot contain non-frozen UDTs
- Altering field types is no longer supported
- Argument ' ' cannot be frozen; remove frozen<> modifier from
- Cannot add new field
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)