apache/seatunnel · critical · SchemaEvolutionException

INVALID_SCHEMA_STRUCTURE

INVALID_SCHEMA_STRUCTURE

Error message

Failed to resolve PostgreSQL RELATION schema change for ${relationId}. Continuing could make the produced row schema diverge from the source relation.

What it means

PostgresRelationSchemaChangeResolver.resolve wraps any unexpected exception (while converting a Debezium RELATION message into a SeaTunnel catalog table) into a SchemaEvolutionException with code INVALID_SCHEMA_STRUCTURE. It signals that the resolver could not derive the new schema for the relation and that continuing would let the produced rows' schema diverge from the actual PostgreSQL relation.

Source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-postgres/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/postgres/source/PostgresRelationSchemaChangeResolver.java:76

        Table after = null;
        CatalogTable before = null;
        try {
            after = extractTable(record);
            before = findCatalogTable(after, catalogTables);
            List<AlterTableColumnEvent> events = resolveAddedColumns(before, after);
            if (events.isEmpty()) {
                return null;
            }

            events.forEach(event -> event.setSourceDialectName(DatabaseIdentifier.POSTGRESQL));
            AlterTableColumnsEvent result = new AlterTableColumnsEvent(before.getTableId(), events);
            result.setSourceDialectName(DatabaseIdentifier.POSTGRESQL);
            return result;
        } catch (SchemaEvolutionException e) {
            throw e;
        } catch (Exception e) {
            String relationId = after == null ? "unknown" : after.id().toString();
            throw new SchemaEvolutionException(
                    SchemaEvolutionErrorCode.INVALID_SCHEMA_STRUCTURE,
                    "Failed to resolve PostgreSQL RELATION schema change for "
                            + relationId
                            + ". Continuing could make the produced row schema diverge from the source relation.",
                    before == null ? null : before.getTableId(),
                    null,
                    e);
        }
    }

    private Table extractTable(SourceRecord record) {
        Struct value = (Struct) record.value();
        List<Struct> changes = value.getArray(HistoryRecord.Fields.TABLE_CHANGES);
        if (changes == null || changes.isEmpty()) {
            throw invalidRelationRecord("PostgreSQL relation record has no table change payload");
        }
        TableChanges tableChanges = new ConnectTableChangeSerializer().deserialize(changes, true);
        return StreamSupport.stream(tableChanges.spliterator(), false)

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Inspect the wrapped cause (`getCause()`) to find the actual conversion failure (usually an unsupported type or null metadata).
  2. Check the table's column types and remove/alter unsupported ones (e.g. custom or unusual types) before the job consumes them.
  3. Restart the pipeline with a fresh replication slot so RELATION messages are re-emitted consistently, or restore from a clean snapshot.
  4. If the failure is persistent for schema-evolution-exempt cases, handle SchemaEvolutionException upstream per your evolution policy.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check relation payload before resolution
if (relationChange == null || relationChange.id() == null) {
    skipEvent();
}

Try / catch

try {
    resolver.resolve(...);
} catch (SchemaEvolutionException e) {
    log.error("RELATION resolution failed for relation; cause=", e.getCause());
    throw e; // do not continue with diverging schema
}

Prevention

When it happens

Trigger: Any exception during RELATION-event schema resolution — e.g. malformed/unsupported column type in the relation message, null `after` payload, or an internal conversion error; `before == null ? null : before.getTableId()` shows it also handles missing prior table info.

Common situations: PostgreSQL DDL introducing types the mapping layer cannot translate; corrupted or partially applied RELATION stream after a slot lsn jump; concurrent DDL during snapshot handoff.

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