apache/seatunnel · error · CatalogException

ColumnAlreadyExistException

Error message

ColumnAlreadyExistException: {}

What it means

SeaTunnel's PaimonCatalog wraps Paimon's org.apache.paimon.catalog.Catalog.ColumnAlreadyExistException and rethrows it as a generic CatalogException with the message "ColumnAlreadyExistException: {}". It means an ALTER TABLE schema change tried to ADD a column whose name already exists in the Paimon table schema. The {} is a literal placeholder that is never substituted (no MessageFormat applied), so the real column name is only available in the wrapped cause.

Solutions

  1. Check the target table's current schema (e.g. via Paimon catalog SHOW CREATE TABLE / schema) and remove or rename the duplicate AddColumn change.
  2. Make the schema change idempotent: skip AddColumn when the column already exists, or use Paimon's ignore-if-not-exists variant of alterTable.
  3. Catch CatalogException in the caller, inspect the cause for ColumnAlreadyExistException, and treat it as a no-op if the existing column is compatible.
  4. Serialize DDL so concurrent jobs cannot add the same column simultaneously.

Example fix

// before
changes.add(SchemaChange.addColumn("amount", DataTypes.DOUBLE()));
catalog.alterTable(identifier, changes);
// after
if (!existingFieldNames.contains("amount")) {
    changes.add(SchemaChange.addColumn("amount", DataTypes.DOUBLE()));
}
catalog.alterTable(identifier, changes);
Defensive patterns

Strategy: validation

Validate before calling

Schema tableSchema = catalog.getTable(identifier);
boolean exists = tableSchema.fields().stream()
        .anyMatch(f -> f.name().equalsIgnoreCase("amount"));
if (exists) { /* skip AddColumn */ }

Try / catch

try {
    catalog.alterTable(identifier, changes);
} catch (CatalogException e) {
    if (e.getCause() instanceof org.apache.paimon.catalog.Catalog.ColumnAlreadyExistException) {
        LOG.warn("column already exists, skipping");
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling catalog.alterTable(identifier, schemaChanges, ignoreIfNotExists) (or the 2-arg overload) with a SchemaChange.AddColumn whose field name already exists in the target Paimon table, and the Paimon catalog is not called with ignoreIfNotExists=true / SchemaChange is not marked as ignore.

Common situations: Running schema-evolution pipelines that add columns idempotently across job restarts; two concurrent jobs both adding the same column; re-running a DDL migration script that was partially applied; human typo where the new column name collides with an existing one (case-sensitivity differences between engines).

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 apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/91a06f33aa2b9854. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-connectors-v2/connector-paimon/src/main/java/org/apache/seatunnel/connectors/seatunnel/paimon/catalog/PaimonCatalog.java:368

        return (PaimonCatalog)
                catalogFactory.createCatalog(catalogFactory.factoryIdentifier(), readonlyConfig);
    }

    // --------------------------------------------------------------------------------------------
    // alterTable
    // --------------------------------------------------------------------------------------------

    public void alterTable(
            Identifier identifier, SchemaChange schemaChange, boolean ignoreIfNotExists) {
        try {
            catalog.alterTable(identifier, schemaChange, ignoreIfNotExists);
        } catch (org.apache.paimon.catalog.Catalog.TableNotExistException e) {
            throw new TableNotExistException(
                    this.catalogName,
                    TablePath.of(identifier.getDatabaseName(), identifier.getTableName()),
                    e);
        } catch (org.apache.paimon.catalog.Catalog.ColumnAlreadyExistException e) {
            throw new CatalogException("ColumnAlreadyExistException: {}", e);
        } catch (org.apache.paimon.catalog.Catalog.ColumnNotExistException e) {
            throw new CatalogException("ColumnNotExistException: {}", e);
        }
    }

    public void alterTable(
            Identifier identifier, List<SchemaChange> schemaChanges, boolean ignoreIfNotExists) {
        try {
            catalog.alterTable(identifier, schemaChanges, ignoreIfNotExists);
        } catch (org.apache.paimon.catalog.Catalog.TableNotExistException e) {
            throw new TableNotExistException(
                    this.catalogName,
                    TablePath.of(identifier.getDatabaseName(), identifier.getTableName()),
                    e);
        } catch (org.apache.paimon.catalog.Catalog.ColumnAlreadyExistException e) {
            throw new CatalogException("ColumnAlreadyExistException: {}", e);
        } catch (org.apache.paimon.catalog.Catalog.ColumnNotExistException e) {
            throw new CatalogException("ColumnNotExistException: {}", e);

View on GitHub (pinned to cf67b549a7)