apache/iceberg · error · UnsupportedOperationException

Unknown row kind:

Error message

Unknown row kind: 

What it means

BaseDeltaTaskWriter.write dispatches RowData by RowKind: INSERT rows go to the data writer, DELETE rows to the delete writer, and UPDATE_BEFORE/UPDATE_AFTER are routed appropriately (before-images ignored or used). Any other RowKind (e.g. a future kind) reaches the default branch and throws this UnsupportedOperationException.

Source

Thrown at flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/sink/BaseDeltaTaskWriter.java:109

        break;

      case UPDATE_BEFORE:
        if (upsert) {
          break; // UPDATE_BEFORE is not necessary for UPSERT, we do nothing to prevent delete one
          // row twice
        }
        writer.delete(row);
        break;
      case DELETE:
        if (upsert) {
          writer.deleteKey(keyProjection.wrap(row));
        } else {
          writer.delete(row);
        }
        break;

      default:
        throw new UnsupportedOperationException("Unknown row kind: " + row.getRowKind());
    }
  }

  protected class RowDataDeltaWriter extends BaseEqualityDeltaWriter {
    RowDataDeltaWriter(PartitionKey partition, PartitioningDVWriter<RowData> dvFileWriter) {
      super(partition, schema, deleteSchema, DeleteGranularity.FILE, dvFileWriter);
    }

    @Override
    protected StructLike asStructLike(RowData data) {
      return wrapper.wrap(data);
    }

    @Override
    protected StructLike asStructLikeKey(RowData data) {
      return keyWrapper.wrap(data);
    }
  }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Normalize incoming rows to INSERT/DELETE/UPDATE_BEFORE/UPDATE_BEFORE kinds before writing to the Iceberg sink.
  2. Align the Flink and Iceberg versions so RowKind enums are consistent.
  3. Audit upstream transformations (e.g. changelog producers) that might emit unexpected row kinds.

Example fix

// before
emit(row) // row with unknown RowKind
// after
if (row.getRowKind() == RowKind.INSERT || row.getRowKind() == RowKind.DELETE) {
  emit(row);
}
Defensive patterns

Strategy: validation

Validate before calling

RowKind kind = row.getRowKind();
if (kind != RowKind.INSERT && kind != RowKind.DELETE
    && kind != RowKind.UPDATE_BEFORE && kind != RowKind.UPDATE_AFTER) {
  throw new IllegalArgumentException("Row kind not supported by Iceberg sink: " + kind);
}

Type guard

boolean writableKind(RowData row) {
  RowKind k = row.getRowKind();
  return k == RowKind.INSERT || k == RowKind.DELETE
      || k == RowKind.UPDATE_BEFORE || k == RowKind.UPDATE_AFTER;
}

Try / catch

try {
  writer.write(row);
} catch (UnsupportedOperationException e) {
  LOG.error("RowKind {} not supported by BaseDeltaTaskWriter", row.getRowKind(), e);
  throw e;
}

Prevention

When it happens

Trigger: Writing a RowData whose getRowKind() is not INSERT, DELETE, UPDATE_BEFORE, or UPDATE_AFTER into an Iceberg Flink sink — practically only possible with custom/updated Flink RowKind values.

Common situations: Custom Flink pipelines that fabricate RowData with unusual row kinds, or running a pipeline built against a newer Flink that introduced a new RowKind into an older Iceberg sink.

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/758ed9763f362f95. Report an issue: GitHub.