apache/iceberg · error · UnsupportedOperationException
Cannot set row kind in the RowDataProjection
Error message
Cannot set row kind in the RowDataProjection
What it means
RowDataProjection is a read-only projecting wrapper around another RowData: it maps a projected Iceberg schema onto the wrapped row. RowKind mutation is not meaningful for a projection, so setRowKind unconditionally throws UnsupportedOperationException. Reading the kind (getRowKind) delegates to the wrapped row, but writing it is rejected.
Solutions
- Mutate the RowKind on the underlying source RowData before wrapping it with RowDataProjection.
- Copy the projected values into a mutable row (e.g. GenericRowData) first, then call setRowKind on that copy.
- Construct the projection from a wrapped row that already carries the correct RowKind; getRowKind() passes it through.
- If you own the code, replace RowDataProjection with a wrapper that delegates setRowKind to the inner row instead of throwing.
Example fix
// before
RowData projected = RowDataProjection.create(schema, projectedSchema).wrap(row);
projected.setRowKind(RowKind.DELETE); // throws
// after
GenericRowData mutable = new GenericRowData(projected.getArity());
for (int i = 0; i < projected.getArity(); i++) {
mutable.setField(i, /* copy field i */ null);
}
mutable.setRowKind(RowKind.DELETE); Defensive patterns
Strategy: fallback
Validate before calling
if (row instanceof RowDataProjection) {
throw new IllegalStateException("Mutate the source RowData, not the projection");
} Type guard
boolean isMutableRow(RowData row) {
return !(row instanceof RowDataProjection);
} Try / catch
try {
projected.setRowKind(RowKind.DELETE);
} catch (UnsupportedOperationException e) {
// projected wrapper is read-only: copy to GenericRowData and set kind there
GenericRowData copy = copyIntoGenericRow(projected);
copy.setRowKind(RowKind.DELETE);
} Prevention
- Treat RowDataProjection as immutable: set RowKind on the wrapped row before projection.
- Never pass projections into Flink operators that mutate RowKind (upsert sinks).
When it happens
Trigger: Calling setRowKind(...) on a RowDataProjection instance obtained from RowDataProjection.create(...).wrap(row), typically when a Flink operator (e.g. changelog-producing sink or process function) tries to mutate the row kind of a projected row.
Common situations: Piping projected rows into a Flink upsert/changelog sink that calls setRowKind; writing generic RowData-manipulating utility code that assumes all RowData implementations are mutable; wrapping rows then trying to tag them with DELETE/UPDATE_BEFORE kinds.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot set row kind in the RowDataProjection
- Cannot set row kind in the RowDataProjection
- Cannot set row kind in the RowDataProjection
- Could not set a field in the RowDataWrapper because rowData…
- Could not set a field in the RowDataWrapper because rowData…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/946ec02e7238abfc.
Report an issue: GitHub.
Appendix: source
Thrown at flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/data/RowDataProjection.java:198
private Object getValue(int pos) {
Preconditions.checkState(rowData != null, "Row data not wrapped");
return getters[pos].getFieldOrNull(rowData);
}
@Override
public int getArity() {
return getters.length;
}
@Override
public RowKind getRowKind() {
Preconditions.checkState(rowData != null, "Row data not wrapped");
return rowData.getRowKind();
}
@Override
public void setRowKind(RowKind kind) {
throw new UnsupportedOperationException("Cannot set row kind in the RowDataProjection");
}
@Override
public boolean isNullAt(int pos) {
return getValue(pos) == null;
}
@Override
public boolean getBoolean(int pos) {
return (boolean) getValue(pos);
}
@Override
public byte getByte(int pos) {
return (byte) getValue(pos);
}
@OverrideView on GitHub (pinned to 86d9c8fc54)