apache/seatunnel · error · SchemaValidationException
UNSUPPORTED_SCHEMA_CHANGE_TYPE
UNSUPPORTED_SCHEMA_CHANGE_TYPE
Error message
PostgreSQL CDC currently supports only ADD COLUMN relation changes. The job stopped before processing rows with the new schema. Restoring the same checkpoint will encounter this relation again until the change is supported or the job is restarted through a controlled schema migration. Cached columns: %s, relation columns: %s
What it means
Postgres CDC's schema-change resolver compares the cached CatalogTable with the live relation and only supports pure ADD COLUMN changes. If the new relation has fewer columns than the cached schema (a drop or rename happened), the job fails fast with UNSUPPORTED_SCHEMA_CHANGE_TYPE. The job stops before processing rows with the new schema so a checkpoint restore does not silently skip or corrupt data.
Source
Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-postgres/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/postgres/source/PostgresRelationSchemaChangeResolver.java:134
.orElseThrow(
() ->
invalidRelationRecord(
"Cannot find cached schema for PostgreSQL table "
+ after.id()));
}
public static String relationSchemaName(Table relation) {
// SeaTunnel's ConnectTableChangeSerializer parses a two-part quoted PostgreSQL identifier
// into TableId.catalog + TableId.table. Prefer the real schema field when present and fall
// back to catalog for synthetic relation records after deserialization.
return relation.id().schema() != null ? relation.id().schema() : relation.id().catalog();
}
private List<AlterTableColumnEvent> resolveAddedColumns(CatalogTable before, Table after) {
List<Column> beforeColumns = before.getTableSchema().getColumns();
if (after.columns().size() < beforeColumns.size()) {
throw unsupportedChange(before, after);
}
for (int i = 0; i < beforeColumns.size(); i++) {
Column beforeColumn = beforeColumns.get(i);
Column afterColumn = convertToSeaTunnelColumn(after, i);
if (!Objects.equals(beforeColumn.getName(), afterColumn.getName())
|| !Objects.equals(beforeColumn.getDataType(), afterColumn.getDataType())
|| beforeColumn.isNullable() != afterColumn.isNullable()) {
throw unsupportedChange(before, after);
}
}
List<AlterTableColumnEvent> events = new ArrayList<>();
for (int i = beforeColumns.size(); i < after.columns().size(); i++) {
Column addedColumn = convertToSeaTunnelColumn(after, i);
AlterTableAddColumnEvent event;
if (i == 0) {
event = AlterTableAddColumnEvent.addFirst(before.getTableId(), addedColumn);View on GitHub (pinned to cf67b549a7)
Solutions
- Undo the schema change on PostgreSQL (re-add the dropped column with the same name/type/nullability) so the relation matches the cached schema, then restore from checkpoint
- Perform a controlled migration: stop the job, rebuild/start it fresh against the new schema (state is schema-bound and cannot resume across a drop)
- Rename strategy: if a rename was intended, express it as ADD new column + backfill, then drop the old column only after switching the job to the new schema
- Upgrade SeaTunnel or file a feature request if DROP/RENAME COLUMN support is needed
Example fix
// before: DROP COLUMN email; on source -> job fails // after: keep old column, add new one ALTER TABLE orders ADD COLUMN user_email text; UPDATE orders SET user_email = email; -- keep 'email' until the CDC job has been migrated
Defensive patterns
Strategy: validation
Validate before calling
// before submitting the CDC job, ensure only additive changes happened
List<String> before = cachedColumns.stream().map(Column::getName).collect(toList());
List<String> after = currentColumns.stream().map(Column::getName).collect(toList());
if (!after.subList(0, before.size()).equals(before))
throw new IllegalStateException("Non-additive schema change detected; migrate manually"); Try / catch
try {
resolver.resolveAddedColumns(before, after);
} catch (SeaTunnelSqlException e) {
if (String.valueOf(e.getMessage()).contains("UNSUPPORTED_SCHEMA_CHANGE_TYPE")
|| String.valueOf(e.getMessage()).contains("only supports ADD COLUMN")) {
// stop pipeline, run controlled schema migration, restart without old checkpoint
}
} Prevention
- Treat existing columns as immutable while CDC jobs are live: never DROP or ALTER them in place
- Use add-column-then-backfill patterns instead of in-place type changes
- Monitor DDL on tracked tables and alert before it lands
- Document a schema-migration runbook: stop job, apply DDL, restart with fresh schema state
When it happens
Trigger: resolveAddedColumns detects after.columns().size() < beforeColumns.size() while comparing the cached CatalogTable against the live PostgreSQL table during change-event handling (events -> resolveAddedColumns).
Common situations: A DBA dropped a column on the source table while the CDC job was running; a column was renamed (seen as drop+add); a table rebuild produced a narrower schema; migration tooling reverted a previous ADD COLUMN.
Related errors
- Unsupported schemaChangeEvent : <eventType>
- Unsupported alter table event:
- Unsupported alter table event:
- DataTypeChanger not reset
- Invalid value for option '" + optionKey + "'. " + e.getMessa
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/88241c5d576e6498.
Report an issue: GitHub.