apache/flink · error · RuntimeException
Could not serialize row '%s'.
Error message
Could not serialize row '%s'.
What it means
The catch-all in DebeziumJsonSerializationSchema.serialize wraps any Throwable raised while building the [before, after, op] GenericRowData and delegating to the internal JsonRowDataSerializationSchema. The root cause is always in the nested 't' — commonly an unsupported RowKind (default branch), a null field where the physical type is non-nullable, or a JSON conversion failure of a field value.
Source
Thrown at flink-formats/flink-json/src/main/java/org/apache/flink/formats/json/debezium/DebeziumJsonSerializationSchema.java:100
case UPDATE_AFTER:
genericRowData.setField(0, null);
genericRowData.setField(1, rowData);
genericRowData.setField(2, OP_INSERT);
return jsonSerializer.serialize(genericRowData);
case UPDATE_BEFORE:
case DELETE:
genericRowData.setField(0, rowData);
genericRowData.setField(1, null);
genericRowData.setField(2, OP_DELETE);
return jsonSerializer.serialize(genericRowData);
default:
throw new UnsupportedOperationException(
format(
"Unsupported operation '%s' for row kind.",
rowData.getRowKind()));
}
} catch (Throwable t) {
throw new RuntimeException(format("Could not serialize row '%s'.", rowData), t);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DebeziumJsonSerializationSchema that = (DebeziumJsonSerializationSchema) o;
return Objects.equals(jsonSerializer, that.jsonSerializer);
}
@Override
public int hashCode() {
return Objects.hash(jsonSerializer);View on GitHub (pinned to 2f3c205e92)
Solutions
- Read the suppressed cause: the RuntimeException's cause (t) holds the real failure — fix that (RowKind, type, or nullability).
- Verify the physical RowType passed to DebeziumJsonSerializationSchema exactly matches the RowData arity and field types produced upstream.
- Recreate/refresh the serialization schema after changing the sink table DDL so codegen matches the new schema.
Defensive patterns
Strategy: try-catch
Validate before calling
// Verify arity/type agreement before the sink
Objects.requireNonNull(row, "row");
if (row.getArity() != expectedPhysicalArity) {
throw new IllegalArgumentException("Row arity " + row.getArity()
+ " != sink arity " + expectedPhysicalArity);
} Try / catch
try { byte[] out = schema.serialize(row); } catch (RuntimeException e) { log.error("serialize failed for row {}, cause: {}", row, e.getCause()); /* dead-letter */ } Prevention
- Log the cause chain, not just the wrapper message.
- Rebuild serialization schemas whenever the sink DDL changes.
When it happens
Trigger: Any Throwable escaping the serialize() try block: unsupported RowKind; a field value whose type cannot be converted by the generated JsonSerializer (e.g. unexpected object); NullPointerException while reading fields of a RowData that does not match the declared physical row type of the sink table.
Common situations: Schema mismatch between the producing job and the debezium-json sink DDL (different field count/order/types); reusing a serialization schema after the sink table schema changed; custom RowData implementations that return null for primitive fields.
Related errors
- Unsupported operation '%s' for row kind.
- Could not serialize row '%s'.
- Can't deserialize Debezium Avro message.
- Could not serialize row '%s'.
- Debezium JSON serialization doesn't support '%s.%s' option b
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/e929c9a7b7af4d2b.
Report an issue: GitHub.