pinpoint-apm/pinpoint · error · IllegalArgumentException

Cannot add an existing column family :

Error message

Cannot add an existing column family : 

What it means

Thrown by TableCommand.applyColumnFamilyChanges when a schema change requests CREATE of a column family that already exists in the target table's descriptor. The library refuses to re-create an existing family instead of silently overwriting its configuration. This is a fail-fast guard for non-idempotent column family additions.

Source

Thrown at hbase/hbase-schema/src/main/java/com/navercorp/pinpoint/hbase/schema/core/command/TableCommand.java:100

        }
        TableConfiguration.Durability durability = tableConfiguration.getDurability();
        if (durability != null) {
            builder.setDurability(Durability.valueOf(durability.name()));
        }
    }

    void applyColumnFamilyChanges(List<ColumnFamilyChange> columnFamilyChanges) {
        if (CollectionUtils.isEmpty(columnFamilyChanges)) {
            return;
        }
        TableDescriptor tableDescriptor = builder.build();

        for (ColumnFamilyChange columnFamilyChange : columnFamilyChanges) {
            ChangeType changeType = columnFamilyChange.getType();
            if (changeType == ChangeType.CREATE) {
                ColumnFamilyDescriptor family = newColumnDescriptor(columnFamilyChange);
                if (tableDescriptor.hasColumnFamily(family.getName())) {
                    throw new IllegalArgumentException("Cannot add an existing column family : " + tableDescriptor.getTableName().getNameAsString());
                }
                builder.setColumnFamily(family);
            } else {
                throw new UnsupportedOperationException("Unknown change type : " + changeType);
            }
        }
    }

    private ColumnFamilyDescriptor newColumnDescriptor(ColumnFamilyChange columnFamilyChange) {
        byte[] name = BytesUtils.toBytes(columnFamilyChange.getName());
        ColumnFamilyDescriptorBuilder builder = ColumnFamilyDescriptorBuilder.newBuilder(name);

        ColumnFamilyConfiguration columnFamilyConfiguration = columnFamilyChange.getColumnFamilyConfiguration();
        Boolean blockCacheEnabled = columnFamilyConfiguration.getBlockCacheEnabled();
        if (blockCacheEnabled != null) {
            builder.setBlockCacheEnabled(blockCacheEnabled);
        }
        Integer replicationScope = columnFamilyConfiguration.getReplicationScope();

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Remove the duplicate CREATE column family entry from the change set, or skip the change set since the family already exists
  2. Express the change as an update/modify of the existing column family instead of CREATE
  3. Check current table state (Admin.getDescriptor) before applying and filter out families that already exist
  4. If the previous run failed midway, verify which changes were applied and start from the correct change set id

Example fix

// before
new ColumnFamilyChange("TestAgent", ChangeType.CREATE, config) // family already on table
// after
if (!admin.getDescriptor(tableName).hasColumnFamily(Bytes.toBytes("TestAgent"))) {
    applyChange(new ColumnFamilyChange("TestAgent", ChangeType.CREATE, config));
}
Defensive patterns

Strategy: validation

Validate before calling

HColumnDescriptor existing = descriptor.getFamily(Bytes.toBytes(familyName));
if (existing != null) {
    throw new IllegalStateException("Column family already exists: " + familyName);
}

Type guard

boolean canCreateFamily(TableDescriptor td, String name) {
    return !td.hasColumnFamily(Bytes.toBytes(name));
}

Try / catch

try {
    command.applyTableChange(change);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Cannot add an existing column family")) {
        // skip or convert to update
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling applyTableChange/applyColumnFamilyChanges with a ColumnFamilyChange of ChangeType.CREATE whose family name is already present in tableDescriptor (tableDescriptor.hasColumnFamily(family.getName()) is true).

Common situations: Re-running a change set that was already partially applied; two change sets both adding the same family name to a table; editing an existing column family's config but expressing it as CREATE instead of a modify operation; stale local schema state vs the actual HBase table.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/8c3089be94cd078c. Report an issue: GitHub.