apache/seatunnel · error · RuntimeException
Fail to deserialize row: ${row}, table: ${tableInfo.getId()}
Error message
Fail to deserialize row: ${row}, table: ${tableInfo.getId()} What it means
This wrapper exception is thrown by SeaTunnelRowStreamingRecordDeserializer.deserialize when the TiKV CDC event row (INSERT/DELETE/UPDATE) fails during conversion into a SeaTunnelRow via the row converter. It wraps the original RuntimeException and annotates it with the raw row and the TiDB table ID so the offending CDC event can be identified. It indicates the change event payload could not be decoded against the captured table schema.
Source
Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-tidb/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/tidb/source/deserializer/SeaTunnelRowStreamingRecordDeserializer.java:77
case PUT:
try {
values =
decodeObjects(
row.getValue().toByteArray(),
RowKey.decode(row.getKey().toByteArray()).getHandle(),
tableInfo);
if (row.getOldValue() == null || row.getOldValue().isEmpty()) {
SeaTunnelRow insert = converter.convert(values, tableInfo, rowType);
insert.setRowKind(RowKind.INSERT);
collect(insert, output);
} else {
SeaTunnelRow update = converter.convert(values, tableInfo, rowType);
update.setRowKind(RowKind.UPDATE_AFTER);
collect(update, output);
}
break;
} catch (final RuntimeException e) {
throw new RuntimeException(
String.format(
"Fail to deserialize row: %s, table: %s",
row, tableInfo.getId()),
e);
}
default:
throw new IllegalArgumentException("Unknown Row Op Type: " + row.getOpType());
}
}
private Object[] decodeDeleteValues(Cdcpb.Event.Row row, long handle) {
ByteString oldValue = row.getOldValue();
if (oldValue != null && !oldValue.isEmpty()) {
return decodeObjects(oldValue.toByteArray(), handle, tableInfo);
}
ByteString value = row.getValue();
if (value != null && !value.isEmpty()) {
// Prefer an available row image before falling back to a PK-only delete row.View on GitHub (pinned to cf67b549a7)
Solutions
- Inspect the wrapped cause ('Caused by') to see the actual decode failure (type/format) and fix the schema mapping
- Restart the job after any DDL change so the table schema is re-captured
- Check the printed table ID and compare its current schema in TiDB with the connector's expected schema
- Upgrade the connector if the failing column type is a newly supported TiDB type
Example fix
// before: deserialize fails mid-job after ALTER TABLE ADD COLUMN converter.convert(values, tableInfo, rowType); // after: capture schema fresh and exclude unsupported columns before starting the CDC source tableInfo = TiDbSchemaUtils.getTableInfo(dataSourceConfig, tableId); // refresh schema job restart with updated table schema
Defensive patterns
Strategy: validation
Validate before calling
// Before starting CDC, verify table schema matches expectations
TableInfo current = TiDbSchemaUtils.getTableInfo(cfg, tableId);
if (!current.getColumns().equals(expectedColumns)) {
throw new IllegalStateException("Schema drift on table " + tableId + "; refresh snapshot");
} Type guard
// Guard values before conversion
if (row.getValue() == null || row.getValue().isEmpty()) {
LOG.warn("CDC row {} for table {} has empty value; skipping", row.getStartTs(), tableInfo.getId());
return;
} Try / catch
try {
deserializer.deserialize(row, output);
} catch (RuntimeException e) {
LOG.error("CDC deserialize failed for table " + tableInfo.getId(), e.getCause());
throw e; // fail job; schema must be re-captured
} Prevention
- Avoid DDL changes on tables during an active CDC job
- Keep connector and TiDB/TiKV versions compatible
- Always inspect the wrapped cause to find the true decode failure
- Pin table schemas in staging before running production CDC
When it happens
Trigger: The CDC event's column values (values/oldValue bytes) do not match the tableInfo schema used by the converter (e.g. schema changed since snapshot, unsupported data type, or malformed/deleted row data from TiKV); converter.convert throws inside the switch statement's try block.
Common situations: Running a CDC job while a DDL change (ALTER TABLE) happens on the source TiDB table; ingesting a table with column types the converter cannot decode; TiKV CDC delivering events for a table whose schema snapshot is stale.
Understand the failure class
Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.
Related errors
- Primary key(%s) is not in table(%s) columns(%s)
- Unknown table change type:
- Unsupported type:
- Unable to convert to LocalDateTime from unexpected value ''
- Unsupported BYTES value type:
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/73f526758a09f723.
Report an issue: GitHub.