{"record":{"id":"e31a87cf8bd263a8","repo":"apache/iceberg","slug":"max-concurrent-deletes-only-works-with-fileios-tha-e31a87","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/procedures/ExpireSnapshotsProcedure.java","lineNumber":150,"sourceCode":"        \"max_concurrent_deletes should have value > 0, value: %s\",\n        maxConcurrentDeletes);\n\n    return modifyIcebergTable(\n        tableIdent,\n        table -> {\n          ExpireSnapshots action = actions().expireSnapshots(table);\n\n          if (olderThanMillis != null) {\n            action.expireOlderThan(olderThanMillis);\n          }\n\n          if (retainLastNum != null) {\n            action.retainLast(retainLastNum);\n          }\n\n          if (maxConcurrentDeletes != null) {\n            if (table.io() instanceof SupportsBulkOperations) {\n              LOG.warn(\n                  \"max_concurrent_deletes only works with FileIOs that do not support bulk deletes. This \"\n                      + \"table is currently using {} which supports bulk deletes so the parameter will be ignored. \"\n                      + \"See that IO's documentation to learn how to adjust parallelism for that particular \"\n                      + \"IO's bulk delete.\",\n                  table.io().getClass().getName());\n            } else {\n\n              action.executeDeleteWith(executorService(maxConcurrentDeletes, \"expire-snapshots\"));\n            }\n          }\n\n          if (snapshotIds != null) {\n            for (long snapshotId : snapshotIds) {\n              action.expireSnapshotId(snapshotId);\n            }\n          }\n\n          if (streamResult != null) {","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/procedures/ExpireSnapshotsProcedure.java#L132-L168","documentation":"WARN from ExpireSnapshotsProcedure.call: the caller passed max_concurrent_deletes, but the table's FileIO implements SupportsBulkOperations, which manages its own bulk-delete parallelism. The option only tunes Tasks.foreach concurrency for non-bulk IOs, so it is ignored.","triggerScenarios":"CALL iceberg.system.expire_snapshots(max_concurrent_deletes => N) on a table whose io() is e.g. S3FileIO/HadoopFileIO implementing SupportsBulkOperations.","commonSituations":"Copy-pasted expire_snapshots tuning from S3a/HadoopFileIO setups onto S3FileIO tables; users unaware bulk deletes (S3 DeleteObjects) control their own concurrency via the IO's properties.","solutions":["Remove max_concurrent_deletes from the procedure call — it has no effect here.","Tune bulk-delete parallelism via the FileIO's own properties (e.g. S3FileIO delete batching/threads in its documented configuration).","Switch the table to a non-bulk FileIO only if you genuinely need the executor-based delete concurrency."],"exampleFix":"// before\nCALL iceberg.system.expire_snapshots(table => 'db.t', max_concurrent_deletes => 20);\n// after: bulk IO handles parallelism itself\nCALL iceberg.system.expire_snapshots(table => 'db.t');","handlingStrategy":"validation","validationCode":"boolean bulk = spark.table(tableName).io() instanceof org.apache.iceberg.io.SupportsBulkOperations; if (!bulk) { /* then max_concurrent_deletes is meaningful */ }","typeGuard":"if (table.io() instanceof SupportsBulkOperations) { /* skip max_concurrent_deletes */ }","tryCatchPattern":null,"preventionTips":["Check whether the table's FileIO supports bulk deletes before tuning delete concurrency","Use the bulk IO's own properties for delete parallelism","Drop the parameter when migrating tables to S3FileIO"],"tags":["logging","spark","procedure","ignored-option","bulk-delete"],"backgroundTag":"ignored-option","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}