apache/flink · error · RuntimeException

Row arity of reuse ({}) does not match this serializer's fie

Error message

Row arity of reuse ({}) does not match this serializer's field length ({}).

What it means

RowSerializer.deserialize(Row reuse, DataInputView) reads a Row from a DataInputView into the provided reuse Row (position-based path). It asserts reuse.getArity() == fieldSerializers.length before reading the null bitmask and field data. If the reuse Row has the wrong arity, this RuntimeException is thrown with both values. The binary format's bitmask is sized to the serializer's field count, so a mismatched reuse Row would corrupt deserialization.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/RowSerializer.java:358

        for (int fieldPos = 0; fieldPos < length; fieldPos++) {
            if (!mask[rowKindOffset + fieldPos]) {
                fieldByPosition[fieldPos] = fieldSerializers[fieldPos].deserialize(source);
            }
        }

        return RowUtils.createRowWithNamedPositions(kind, fieldByPosition, positionByName);
    }

    @Override
    public Row deserialize(Row reuse, DataInputView source) throws IOException {
        // reuse uses name-based field mode, do a non-reuse deserialize
        if (reuse == null || reuse.getFieldNames(false) != null) {
            return deserialize(source);
        }
        final int length = fieldSerializers.length;

        if (reuse.getArity() != length) {
            throw new RuntimeException(
                    "Row arity of reuse ("
                            + reuse.getArity()
                            + ") does not match "
                            + "this serializer's field length ("
                            + length
                            + ").");
        }

        // read bitmask
        readIntoMask(source, mask);
        if (supportsRowKind) {
            reuse.setKind(readKindFromMask(mask));
        }

        // deserialize fields
        for (int fieldPos = 0; fieldPos < length; fieldPos++) {
            if (mask[rowKindOffset + fieldPos]) {
                reuse.setField(fieldPos, null);

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Allocate the reuse Row with exactly fieldSerializers.length fields: new Row(serializer.getArity()).
  2. Do not reuse Row objects across serializers with different arities.
  3. After a schema change, discard old reuse Row buffers and reallocate to the new arity.
  4. Use RowSerializer.createInstance() to obtain a correctly-sized reuse Row.

Example fix

// before — reuse Row arity mismatch
RowSerializer ser = new RowSerializer(new TypeSerializer[]{intSer, strSer}); // arity 2
Row reuse = new Row(3); // wrong
ser.deserialize(reuse, input); // arity 3 ≠ 2 → exception

// after — reuse Row matches serializer arity
Row reuse = ser.createInstance(); // arity 2, correctly sized
ser.deserialize(reuse, input); // OK
Defensive patterns

Strategy: validation

Validate before calling

// Validate reuse arity before deserialize
public static Row safeDeserialize(RowSerializer ser, Row reuse, DataInputView in)
        throws IOException {
    if (reuse.getArity() != ser.getArity()) {
        throw new IllegalArgumentException(
            "Reuse Row arity " + reuse.getArity()
            + " != serializer arity " + ser.getArity());
    }
    return ser.deserialize(reuse, in);
}

Try / catch

try {
    Row result = serializer.deserialize(reuse, input);
} catch (RuntimeException e) {
    if (e.getMessage().contains("Row arity of reuse")) {
        log.error("Reuse arity {} != expected {}; reallocating",
            reuse.getArity(), serializer.getArity());
        reuse = serializer.createInstance();
        result = serializer.deserialize(reuse, input);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: RowSerializer.deserialize(Row reuse, DataInputView) is called (e.g., during network read, state restore, or shuffle receive) with a reuse Row whose arity does not equal the serializer's field count.

Common situations: The reuse Row was pre-allocated for a different schema (different number of fields); a Row reuse pool sized for an old arity is still in use after a schema change; state restore from a savepoint where the reuse buffer was created for a previous Row schema; an operator reuses a Row object from a different source with a different arity.

Related errors


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