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
- Remove max_concurrent_deletes and configure the IO's bulk-delete parallelism instead (e.g. S3FileIO delete batch size / thread pool properties)
- Keep the option only for non-bulk IOs where it maps to executor threads
- 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
- Check the table's FileIO type before setting max_concurrent_deletes
- Use IO-native properties (e.g. S3FileIO bulk delete settings) for parallelism
- Avoid copying HadoopFileIO-era tuning params to S3/GCS tables
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
- max_concurrent_deletes only works with FileIOs that do not s
- max_concurrent_deletes only works with FileIOs that do not s
- max_concurrent_deletes only works with FileIOs that do not s
- Procedure ${ident} not found
- Couldn't load table '%s' in catalog '%s'
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/b3871dcac53d3b44.
Report an issue: GitHub.