apache/seatunnel · error · IllegalStateException

The "before" field of ${op} operation is null, if you are us

Error message

The "before" field of ${op} operation is null, if you are using Debezium Postgres Connector, please check the Postgres table has been set REPLICA IDENTITY to FULL level.

What it means

When decoding a Debezium UPDATE event, the converter needs the payload's 'before' image to emit the UPDATE_BEFORE row. If payload.before is missing or null, DebeziumJsonDeserializationSchema throws IllegalStateException via the REPLICA_IDENTITY_EXCEPTION template. For Postgres this means the table's REPLICA IDENTITY is not FULL, so the WAL change event carries no old row values.

Source

Thrown at seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/debezium/DebeziumJsonDeserializationSchema.java:140

        JsonNode tsNode = payload.get(DATA_TS);

        switch (op) {
            case OP_CREATE:
            case OP_READ:
                SeaTunnelRow insert = debeziumRowConverter.parse(payload.get(DATA_AFTER));
                insert.setRowKind(RowKind.INSERT);
                if (tablePath != null) {
                    insert.setTableId(tablePath.toString());
                }
                if (tsNode != null) {
                    MetadataUtil.setEventTime(insert, tsNode.asLong());
                }
                out.collect(insert);
                break;
            case OP_UPDATE:
                SeaTunnelRow before = debeziumRowConverter.parse(payload.get(DATA_BEFORE));
                if (before == null) {
                    throw new IllegalStateException(
                            String.format(REPLICA_IDENTITY_EXCEPTION, "UPDATE"));
                }
                before.setRowKind(RowKind.UPDATE_BEFORE);
                if (tablePath != null) {
                    before.setTableId(tablePath.toString());
                }
                if (tsNode != null) {
                    MetadataUtil.setEventTime(before, tsNode.asLong());
                }

                SeaTunnelRow after = debeziumRowConverter.parse(payload.get(DATA_AFTER));
                after.setRowKind(RowKind.UPDATE_AFTER);

                if (tablePath != null) {
                    after.setTableId(tablePath.toString());
                }
                if (tsNode != null) {
                    MetadataUtil.setEventTime(after, tsNode.asLong());

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Run ALTER TABLE <table> REPLICA IDENTITY FULL; on the source Postgres table
  2. Verify with SELECT relreplident FROM pg_class WHERE relname='<table>'; — it should show 'f' (FULL)
  3. If FULL is infeasible, configure the pipeline to skip UPDATE_BEFORE (e.g. handle only after-images downstream)
  4. For non-Postgres sources, inspect the Debezium connector config (converters/SMTs) for anything dropping the before field

Example fix

// psql, before capturing CDC
ALTER TABLE my_table REPLICA IDENTITY FULL;
Defensive patterns

Strategy: validation

Validate before calling

-- pre-flight check on the source table
SELECT relreplident FROM pg_class WHERE relname = 'my_table'; -- must be 'f'

Try / catch

try {
    deserializer.deserialize(message, out);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("REPLICA IDENTITY")) {
        log.error("Set REPLICA IDENTITY FULL on the source table and re-capture", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Consuming Debezium Postgres UPDATE events (op='u') where payload.get(DATA_BEFORE) returns null — i.e. the source table has default replica identity (DEFAULT or NOTHING).

Common situations: Postgres logical replication on tables never configured for FULL replica identity; tables whose primary key exists but replica identity is default; custom Debezium converters that drop the before-image.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/57231339066b195f. Report an issue: GitHub.