{"record":{"id":"e853ebf1b66bc3d9","repo":"apache/iceberg","slug":"cannot-set-row-kind-in-the-rowdataprojection-e853eb","errorCode":null,"errorMessage":"Cannot set row kind in the RowDataProjection","messagePattern":"Cannot set row kind in the RowDataProjection","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"flink/v2.3/flink/src/main/java/org/apache/iceberg/flink/data/RowDataProjection.java","lineNumber":198,"sourceCode":"  private Object getValue(int pos) {\n    Preconditions.checkState(rowData != null, \"Row data not wrapped\");\n    return getters[pos].getFieldOrNull(rowData);\n  }\n\n  @Override\n  public int getArity() {\n    return getters.length;\n  }\n\n  @Override\n  public RowKind getRowKind() {\n    Preconditions.checkState(rowData != null, \"Row data not wrapped\");\n    return rowData.getRowKind();\n  }\n\n  @Override\n  public void setRowKind(RowKind kind) {\n    throw new UnsupportedOperationException(\"Cannot set row kind in the RowDataProjection\");\n  }\n\n  @Override\n  public boolean isNullAt(int pos) {\n    return getValue(pos) == null;\n  }\n\n  @Override\n  public boolean getBoolean(int pos) {\n    return (boolean) getValue(pos);\n  }\n\n  @Override\n  public byte getByte(int pos) {\n    return (byte) getValue(pos);\n  }\n\n  @Override","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v2.3/flink/src/main/java/org/apache/iceberg/flink/data/RowDataProjection.java#L180-L216","documentation":"RowDataProjection wraps a projected view over another RowData and deliberately forbids mutating the row kind: setRowKind(RowKind) unconditionally throws UnsupportedOperationException. Row kind (INSERT/DELETE/UPDATE_BEFORE/UPDATE_AFTER) belongs to the underlying source row, not a projection of it.","triggerScenarios":"Calling setRowKind(...) on a RowDataProjection instance — e.g. generic Flink code that assumes every RowData is mutable (as with GenericRowData) and tries to change the changelog kind on a projected row.","commonSituations":"Flink pipeline code applying row-kind updates (dedup, CDC processing) to rows after they passed through a projection; frameworks that mutate RowData in place hitting the wrapper instead of the original row.","solutions":["Call setRowKind on the underlying/original RowData (the one wrapped by the projection) instead of the projection itself.","Copy the projected fields into a new GenericRowData and set the RowKind on that copy.","Restructure the pipeline to apply row-kind changes before projection."],"exampleFix":"// before\nprojectedRow.setRowKind(RowKind.DELETE);\n// after\nGenericRowData out = new GenericRowData(projectedRow.getArity());\nfor (int i = 0; i < projectedRow.getArity(); i++) out.setField(i, RowData.createFieldGetter(...));\nout.setRowKind(RowKind.DELETE);","handlingStrategy":"try-catch","validationCode":"// java\nif (row instanceof RowDataProjection) {\n  throw new IllegalStateException(\"setRowKind is unsupported on RowDataProjection; mutate the source row instead\");\n}","typeGuard":"// java\nRowData mutableRow = (row instanceof RowDataProjection)\n    ? null  // projection is immutable; use the original row\n    : row;","tryCatchPattern":"// java\ntry {\n  row.setRowKind(RowKind.INSERT);\n} catch (UnsupportedOperationException e) {\n  // fall back: copy fields into GenericRowData and set kind on the copy\n}","preventionTips":["Treat RowDataProjection as a read-only view; only read fields and getRowKind from it.","Set RowKind on the upstream source RowData before projection, or build a fresh GenericRowData for output.","Avoid generic code that blindly mutates any RowData instance."],"tags":["flink","rowdata","immutable","projection"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}