apache/iceberg · warning

max_concurrent_deletes only works with FileIOs that do not s

Error message

max_concurrent_deletes only works with FileIOs that do not support bulk deletes. This table is currently using {} which supports bulk deletes so the parameter will be ignored. See that IO's documentation to learn how to adjust parallelism for that particular IO's bulk delete.

What it means

A LOG.warn in ExpireSnapshotsProcedure.call: the max_concurrent_deletes parameter only controls thread pool size for FileIO implementations without bulk-delete support. When the table's FileIO implements SupportsBulkOperations (e.g. S3FileIO, GCSFileIO), parallelism is governed by the IO's own bulk-delete configuration and the procedure parameter is ignored.

Source

Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/procedures/ExpireSnapshotsProcedure.java:150

        "max_concurrent_deletes should have value > 0, value: %s",
        maxConcurrentDeletes);

    return modifyIcebergTable(
        tableIdent,
        table -> {
          ExpireSnapshots action = actions().expireSnapshots(table);

          if (olderThanMillis != null) {
            action.expireOlderThan(olderThanMillis);
          }

          if (retainLastNum != null) {
            action.retainLast(retainLastNum);
          }

          if (maxConcurrentDeletes != null) {
            if (table.io() instanceof SupportsBulkOperations) {
              LOG.warn(
                  "max_concurrent_deletes only works with FileIOs that do not support bulk deletes. This "
                      + "table is currently using {} which supports bulk deletes so the parameter will be ignored. "
                      + "See that IO's documentation to learn how to adjust parallelism for that particular "
                      + "IO's bulk delete.",
                  table.io().getClass().getName());
            } else {

              action.executeDeleteWith(executorService(maxConcurrentDeletes, "expire-snapshots"));
            }
          }

          if (snapshotIds != null) {
            for (long snapshotId : snapshotIds) {
              action.expireSnapshotId(snapshotId);
            }
          }

          if (streamResult != null) {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Remove max_concurrent_deletes for tables using bulk-capable IOs; it has no effect.
  2. Tune the IO's own bulk-delete parallelism instead — e.g. S3FileIO's spark.s3.delete.batch-size / s3.delete.batch-size properties and the underlying client's concurrency settings.
  3. Consult the specific FileIO implementation's documentation for its bulk-delete concurrency knobs.

Example fix

// before
CALL catalog.system.expire_snapshots(table => 'db.t', max_concurrent_deletes => 20)
-- table io = S3FileIO (bulk deletes)
// after
CALL catalog.system.expire_snapshots(table => 'db.t')
-- tune IO instead: SET TABLE PROPERTIES ('s3.delete.batch-size'='...') or client config
Defensive patterns

Strategy: validation

Validate before calling

if (table.io() instanceof SupportsBulkOperations) {
  // do not pass max_concurrent_deletes; tune IO bulk-delete settings instead
}

Type guard

boolean ioSupportsBulkDeletes = table.io() instanceof org.apache.iceberg.io.SupportsBulkOperations;

Prevention

When it happens

Trigger: CALL catalog.system.expire_snapshots(..., max_concurrent_deletes => N) on a table whose io() instanceof SupportsBulkOperations (e.g. S3FileIO).

Common situations: Migrating from HadoopFileIO to S3FileIO and carrying over old tuning options; copy-pasted procedure calls across tables with different IOs.

Related errors


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