apache/iceberg · info

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

ExpireSnapshotsProcedure warns that the max_concurrent_deletes option only controls parallelism for FileIOs without bulk delete support. When the table's IO implements SupportsBulkOperations (e.g. S3FileIO), the option is ignored and the IO's own bulk-delete concurrency settings apply.

Source

Thrown at spark/v4.1/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 and configure the IO's bulk-delete parallelism instead (e.g. S3FileIO delete batch size / thread pool properties)
  2. Keep the option only for non-bulk IOs where it maps to executor threads
  3. Check the FileIO implementation's documentation for its parallelism knobs

Example fix

// before
CALL iceberg.system.expire_snapshots(table => 'db.t', max_concurrent_deletes => 16)
// after
-- set io-level property, e.g.
ALTER TABLE db.t SET TBLPROPERTIES ('io.manifest.cache-enabled'='true'); -- plus IO bulk-delete properties
CALL iceberg.system.expire_snapshots(table => 'db.t')
Defensive patterns

Strategy: validation

Validate before calling

if (maxConcurrentDeletes != null && table.io() instanceof SupportsBulkOperations) {
  // skip passing max_concurrent_deletes; configure IO bulk-delete properties instead
}

Type guard

boolean bulkSupported = table.io() instanceof SupportsBulkOperations;

Prevention

When it happens

Trigger: Calling CALL iceberg.system.expire_snapshots(...) with max_concurrent_deletes set on a table whose io() instanceof SupportsBulkOperations.

Common situations: Users migrating configs from HadoopFileIO tables to S3/GCS tables keep max_concurrent_deletes and are surprised it no longer has effect.

Related errors


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