{"record":{"id":"4b6f9d7a23c2fde0","repo":"apache/seatunnel","slug":"unknown-row-op-type-row-getoptype","errorCode":null,"errorMessage":"Unknown Row Op Type: ${row.getOpType()}","messagePattern":"Unknown Row Op Type: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-cdc/connector-cdc-tidb/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/tidb/source/deserializer/SeaTunnelRowStreamingRecordDeserializer.java","lineNumber":84,"sourceCode":"                    if (row.getOldValue() == null || row.getOldValue().isEmpty()) {\n                        SeaTunnelRow insert = converter.convert(values, tableInfo, rowType);\n                        insert.setRowKind(RowKind.INSERT);\n                        collect(insert, output);\n                    } else {\n                        SeaTunnelRow update = converter.convert(values, tableInfo, rowType);\n                        update.setRowKind(RowKind.UPDATE_AFTER);\n                        collect(update, output);\n                    }\n                    break;\n                } catch (final RuntimeException e) {\n                    throw new RuntimeException(\n                            String.format(\n                                    \"Fail to deserialize row: %s, table: %s\",\n                                    row, tableInfo.getId()),\n                            e);\n                }\n            default:\n                throw new IllegalArgumentException(\"Unknown Row Op Type: \" + row.getOpType());\n        }\n    }\n\n    private Object[] decodeDeleteValues(Cdcpb.Event.Row row, long handle) {\n        ByteString oldValue = row.getOldValue();\n        if (oldValue != null && !oldValue.isEmpty()) {\n            return decodeObjects(oldValue.toByteArray(), handle, tableInfo);\n        }\n        ByteString value = row.getValue();\n        if (value != null && !value.isEmpty()) {\n            // Prefer an available row image before falling back to a PK-only delete row.\n            return decodeObjects(value.toByteArray(), handle, tableInfo);\n        }\n        return decodeDeleteValuesFromHandle(row, handle);\n    }\n\n    private Object[] decodeDeleteValuesFromHandle(Cdcpb.Event.Row row, long handle) {\n        TiColumnInfo handleColumn = tableInfo.getPKIsHandleColumn();","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-cdc/connector-cdc-tidb/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/tidb/source/deserializer/SeaTunnelRowStreamingRecordDeserializer.java#L66-L102","documentation":"Thrown by SeaTunnelRowStreamingRecordDeserializer.deserialize when a TiKV CDC event row carries an op type (RowOpType) that the switch statement does not handle — only PUT, DELETE and UPDATE-after-PUT variants are expected. It signals an unknown/unsupported operation type in the CDC protocol event.","triggerScenarios":"The Cdcpb.Event.Row.getOpType() returns a value outside the handled enum cases, typically due to a TiKV/TiCDC protocol version mismatch or a newly introduced op type the connector does not yet support.","commonSituations":"Deploying a connector built against an older TiKV client proto against a newer TiDB/TiKV cluster that emits new op types; corrupted or partially parsed CDC event stream.","solutions":["Upgrade connector-cdc-tidb (and its bundled TiKV client proto) to match your TiDB cluster version","Log and inspect row.getOpType() to identify the unhandled value","Downgrade/check TiCDC compatibility if you cannot upgrade the connector","File/verify support for the missing op type in the connector's deserialize switch"],"exampleFix":"// before\nthrow new IllegalArgumentException(\"Unknown Row Op Type: \" + row.getOpType());\n// after: upgrade connector version so all TiKV RowOpType values are handled in the switch","handlingStrategy":"type-guard","validationCode":"// Check op type before deserializing\nObject opType = row.getOpType();\nif (opType != RowOpType.PUT && opType != RowOpType.DELETE) {\n    LOG.warn(\"Skipping unhandled RowOpType {} at ts {}\", opType, row.getCommitTs());\n}","typeGuard":"static boolean isSupportedOp(Cdcpb.Event.Row row) {\n    return row.getOpType() == RowOpType.PUT\n        || row.getOpType() == RowOpType.DELETE;\n}","tryCatchPattern":"try {\n    deserializer.deserialize(row, output);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Unknown Row Op Type\")) {\n        LOG.warn(\"Skipping unknown op type: {}\", row.getOpType());\n    } else { throw e; }\n}","preventionTips":["Match connector TiKV proto version to your TiDB cluster version","Log row.getOpType() on unknown values to detect protocol drift early","Subscribe to TiDB release notes for new CDC op types"],"tags":["cdc","tidb","unsupported-op-type","protocol-version"],"backgroundTag":"invalid-enum-value","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}