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

When expiring snapshots, max_concurrent_deletes only tunes delete parallelism for FileIOs that delete files one-by-one. If the table's IO implements SupportsBulkOperations (e.g. S3FileIO, GCSFileIO), the parameter is ignored and this is logged.

Source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/procedures/ExpireSnapshotsProcedure.java:145

        "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 from the procedure call
  2. Tune the bulk-delete parallelism via the specific FileIO's own properties (e.g. s3.delete.batch-size / filesystem concurrency settings)
  3. Use a FileIO without bulk-delete support only if you need max_concurrent_deletes to take effect

Example fix

// before
CALL iceberg.system.expire_snapshots(table => 'db.t', max_concurrent_deletes => 20)
-- after
CALL iceberg.system.expire_snapshots(table => 'db.t')  -- plus e.g. s3.delete.batch-size in table properties
Defensive patterns

Strategy: validation

Validate before calling

if (table.io() instanceof org.apache.iceberg.io.SupportsBulkOperations) { /* don't pass max_concurrent_deletes */ }

Type guard

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

Prevention

When it happens

Trigger: CALL iceberg.system.expire_snapshots(...) with max_concurrent_deletes set while table.io() instanceof SupportsBulkOperations.

Common situations: Users tune thread counts assuming the setting applies to S3/GCS/Azure bulk deletes; those IOs control bulk-delete parallelism via their own properties (e.g. s3.delete.batch-size, concurrency settings).

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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