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

  1. Read the suppressed cause: the RuntimeException's cause (t) holds the real failure — fix that (RowKind, type, or nullability).
  2. Verify the physical RowType passed to DebeziumJsonSerializationSchema exactly matches the RowData arity and field types produced upstream.
  3. 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

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


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/e929c9a7b7af4d2b. Report an issue: GitHub.