{"record":{"id":"57231339066b195f","repo":"apache/seatunnel","slug":"the-before-field-of-op-operation-is-null-if","errorCode":null,"errorMessage":"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.","messagePattern":"The \"before\" field of (.+?) operation is null, if you are using Debezium Postgres Connector, please check the Postgres table has been set REPLICA IDENTITY to FULL level\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/debezium/DebeziumJsonDeserializationSchema.java","lineNumber":140,"sourceCode":"        JsonNode tsNode = payload.get(DATA_TS);\n\n        switch (op) {\n            case OP_CREATE:\n            case OP_READ:\n                SeaTunnelRow insert = debeziumRowConverter.parse(payload.get(DATA_AFTER));\n                insert.setRowKind(RowKind.INSERT);\n                if (tablePath != null) {\n                    insert.setTableId(tablePath.toString());\n                }\n                if (tsNode != null) {\n                    MetadataUtil.setEventTime(insert, tsNode.asLong());\n                }\n                out.collect(insert);\n                break;\n            case OP_UPDATE:\n                SeaTunnelRow before = debeziumRowConverter.parse(payload.get(DATA_BEFORE));\n                if (before == null) {\n                    throw new IllegalStateException(\n                            String.format(REPLICA_IDENTITY_EXCEPTION, \"UPDATE\"));\n                }\n                before.setRowKind(RowKind.UPDATE_BEFORE);\n                if (tablePath != null) {\n                    before.setTableId(tablePath.toString());\n                }\n                if (tsNode != null) {\n                    MetadataUtil.setEventTime(before, tsNode.asLong());\n                }\n\n                SeaTunnelRow after = debeziumRowConverter.parse(payload.get(DATA_AFTER));\n                after.setRowKind(RowKind.UPDATE_AFTER);\n\n                if (tablePath != null) {\n                    after.setTableId(tablePath.toString());\n                }\n                if (tsNode != null) {\n                    MetadataUtil.setEventTime(after, tsNode.asLong());","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/debezium/DebeziumJsonDeserializationSchema.java#L122-L158","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Run ALTER TABLE <table> REPLICA IDENTITY FULL; on the source Postgres table","Verify with SELECT relreplident FROM pg_class WHERE relname='<table>'; — it should show 'f' (FULL)","If FULL is infeasible, configure the pipeline to skip UPDATE_BEFORE (e.g. handle only after-images downstream)","For non-Postgres sources, inspect the Debezium connector config (converters/SMTs) for anything dropping the before field"],"exampleFix":"// psql, before capturing CDC\nALTER TABLE my_table REPLICA IDENTITY FULL;","handlingStrategy":"validation","validationCode":"-- pre-flight check on the source table\nSELECT relreplident FROM pg_class WHERE relname = 'my_table'; -- must be 'f'","typeGuard":null,"tryCatchPattern":"try {\n    deserializer.deserialize(message, out);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"REPLICA IDENTITY\")) {\n        log.error(\"Set REPLICA IDENTITY FULL on the source table and re-capture\", e);\n    }\n    throw e;\n}","preventionTips":["ALTER TABLE ... REPLICA IDENTITY FULL for all CDC-captured Postgres tables","Verify relreplident='f' as part of deployment","Document replica-identity requirements in runbooks"],"tags":["cdc","debezium","postgres","replica-identity","update"],"backgroundTag":"missing-required-argument","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}