prestodb/presto · error · PrestoException

ICEBERG_INVALID_FORMAT_VERSION

ICEBERG_INVALID_FORMAT_VERSION

Error message

Iceberg table updates require at least format version 2 and update mode must be merge-on-read

What it means

Thrown when a row-level update targets an Iceberg table that either has format version below 2 (no delete files) or is not configured with update.mode=merge-on-read. Row-level DML requires v2 semantics and merge-on-read mode in this connector.

Source

Thrown at presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergAbstractMetadata.java:939

    public ConnectorMergeTableHandle beginMerge(ConnectorSession session, ConnectorTableHandle tableHandle)
    {
        shouldRunInAutoCommitTransaction("MERGE INTO");
        IcebergTableHandle icebergTableHandle = (IcebergTableHandle) tableHandle;
        verify(icebergTableHandle.getIcebergTableName().getTableType() == DATA, "only the data table can have data merged");
        Table icebergTable = getIcebergTable(session, icebergTableHandle.getSchemaTableName());
        validateBranchExists(icebergTableHandle, icebergTable);
        int formatVersion = ((BaseTable) icebergTable).operations().current().formatVersion();

        if (formatVersion > MAX_FORMAT_VERSION_FOR_ROW_LEVEL_OPERATIONS) {
            throw new PrestoException(NOT_SUPPORTED,
                    format("Iceberg table updates for format version %s are not supported yet", formatVersion));
        }

        if (formatVersion < MIN_FORMAT_VERSION_FOR_DELETE ||
                !Optional.ofNullable(icebergTable.properties().get(TableProperties.UPDATE_MODE))
                        .map(mode -> mode.equals(MERGE_ON_READ.modeName()))
                        .orElse(false)) {
            throw new PrestoException(ICEBERG_INVALID_FORMAT_VERSION,
                    "Iceberg table updates require at least format version 2 and update mode must be merge-on-read");
        }
        validateTableMode(session, icebergTable);

        IcebergInsertTableHandle insertHandle = new IcebergInsertTableHandle(
                icebergTableHandle.getSchemaName(),
                icebergTableHandle.getIcebergTableName(),
                toPrestoSchema(icebergTable.schema(), typeManager),
                toPrestoPartitionSpec(icebergTable.spec(), typeManager),
                getColumns(icebergTable.schema(), icebergTable.spec(), typeManager),
                icebergTable.location(),
                getFileFormat(icebergTable),
                getCompressionCodec(session),
                icebergTable.properties(),
                getSupportedSortFields(icebergTable.schema(), icebergTable.sortOrder()),
                Optional.empty());

        Map<Integer, PrestoIcebergPartitionSpec> partitionSpecs = transformValues(icebergTable.specs(), partitionSpec -> toPrestoPartitionSpec(partitionSpec, typeManager));

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Set the table property update.mode=merge-on-read, e.g. ALTER TABLE ... SET TBLPROPERTIES ('update.mode'='merge-on-read').
  2. Upgrade the table to format version 2 (rewrite the table or set format-version via a supported writer).
  3. Recreate the table with format-version=2 and merge-on-read mode before running row-level DML.

Example fix

// before
MERGE INTO t ...; -- t is format version 1 / copy-on-write
// after
ALTER TABLE t SET TBLPROPERTIES ('update.mode' = 'merge-on-read', 'format-version' = '2');
MERGE INTO t ...;
Defensive patterns

Strategy: validation

Validate before calling

-- verify format version and update mode before MERGE/UPDATE/DELETE
SELECT key, value FROM "t$properties" WHERE key IN ('format-version', 'update.mode');
-- proceed only if format-version >= 2 and update.mode = merge-on-read

Try / catch

try { merge(...); }
catch (PrestoException e) { if (ICEBERG_INVALID_FORMAT_VERSION.equals(e.getErrorCode())) { /* set table properties and retry */ } }

Prevention

When it happens

Trigger: Running MERGE/UPDATE/DELETE against a format-version-1 table, or a v2 table whose table property update.mode is not set to merge-on-read (e.g. copy-on-write).

Common situations: Legacy tables created before the engine supported v2; tables created with copy-on-write defaults; properties changed or never set after upgrading Presto.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/a87db10809284ba4. Report an issue: GitHub.