apache/flink · error · RuntimeException

Serializer does not support named field positions.

Error message

Serializer does not support named field positions.

What it means

RowSerializer.copyNameBased() is invoked when the source Row has named fields (getFieldNames() returns non-null). It requires the serializer to have been constructed with a positionByName map (field-name → position). If positionByName is null — meaning the serializer was built for position-based Rows only — this RuntimeException is thrown. The serializer cannot map named fields to positions it was never given.

Source

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

                            + "this serializer's field length ("
                            + length
                            + ").");
        }
        final Object[] fieldByPosition = new Object[length];
        for (int i = 0; i < length; i++) {
            final Object fromField = from.getField(i);
            if (fromField != null) {
                final Object copy = fieldSerializers[i].copy(fromField);
                fieldByPosition[i] = copy;
            }
        }
        return RowUtils.createRowWithNamedPositions(
                from.getKind(), fieldByPosition, positionByName);
    }

    private Row copyNameBased(Row from, Set<String> fieldNames) {
        if (positionByName == null) {
            throw new RuntimeException("Serializer does not support named field positions.");
        }
        final Row newRow = Row.withNames(from.getKind());
        for (String fieldName : fieldNames) {
            final int targetPos = getPositionByName(fieldName);
            final Object fromField = from.getField(fieldName);
            if (fromField != null) {
                final Object copy = fieldSerializers[targetPos].copy(fromField);
                newRow.setField(fieldName, copy);
            } else {
                newRow.setField(fieldName, null);
            }
        }
        return newRow;
    }

    @Override
    public Row copy(Row from, Row reuse) {
        // cannot reuse, do a non-reuse copy

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Construct the RowSerializer (or RowTypeInfo) WITH field names so positionByName is populated: pass a LinkedHashMap<String,Integer> as the second constructor argument.
  2. Ensure all Rows in the pipeline use the same mode (all position-based or all name-based consistently).
  3. If the data is truly name-based, build the TypeInformation with named fields so the serializer is constructed with the name→position map.
  4. Convert name-based Rows to position-based before serializing if the serializer cannot be changed.

Example fix

// before — position-based serializer receiving a name-based Row
RowSerializer ser = new RowSerializer(new TypeSerializer[]{intSer, strSer});
Row named = Row.withNames().setField("id", 1).setField("name", "a");
ser.copy(named); // positionByName == null → exception

// after — construct serializer with field-name map
LinkedHashMap<String, Integer> names = new LinkedHashMap<>();
names.put("id", 0); names.put("name", 1);
RowSerializer ser = new RowSerializer(new TypeSerializer[]{intSer, strSer}, names);
ser.copy(named); // positionByName != null → OK
Defensive patterns

Strategy: type-guard

Validate before calling

// Check whether the serializer supports name-based Rows before using them
public static boolean supportsNamedFields(RowSerializer ser) {
    // positionByName is private; infer by testing with createInstance
    Row probe = ser.createInstance();
    return probe.getFieldNames(false) != null;
}

// Or simply ensure you always construct with names when needed
public static RowSerializer withNames(TypeSerializer<?>[] sers, String... names) {
    LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
    for (int i = 0; i < names.length; i++) map.put(names[i], i);
    return new RowSerializer(sers, map);
}

Type guard

public static boolean isNameBasedRow(Row row) {
    return row.getFieldNames(false) != null;
}

// Before serializing a name-based Row, ensure the serializer was built with names:
public static boolean serializerSupportsNames(RowSerializer ser) {
    return ser.createInstance().getFieldNames(false) != null;
}

Try / catch

try {
    serializer.copy(namedRow);
} catch (RuntimeException e) {
    if (e.getMessage().contains("does not support named field positions")) {
        log.error("Serializer is position-based; rebuild with field names or "
            + "convert the Row to position-based mode");
    }
    throw e;
}

Prevention

When it happens

Trigger: RowSerializer.copy(Row) receives a name-based Row (created via Row.withNames()), but the RowSerializer was constructed via the single-argument constructor RowSerializer(TypeSerializer<?>[]) which sets positionByName = null.

Common situations: The RowTypeInfo was built without field names, but the data stream carries name-based Rows (e.g., produced by a source that emits Row.withNames()); a Table-to-DataStream bridge or a connector emits named Rows into a pipeline whose serializer was configured for positional Rows; mixing name-based and position-based Row creation in the same pipeline.

Related errors


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