apache/iceberg · warning
Bulk deletion failed for
Error message
Bulk deletion failed for {} of {} {} file(s) What it means
A WARN from FileCleanupStrategy.deleteFiles (used by expireSnapshots and orphan/planning cleanup jobs) when bulk deletion via SupportsBulkOperations reports BulkDeletionFailureException: a subset of the pathsToDelete could not be deleted, with counts given in the message. Cleanup proceeds as best-effort; the failed files remain on storage until a future cleanup run deletes them.
Solutions
- Re-run expireSnapshots/cleanup — already-deleted files are skipped and remaining ones retried.
- Throttle cleanup concurrency or run during off-peak hours to avoid provider rate limiting.
- Ensure only one cleanup job runs per table at a time to avoid racing deletions.
- Review IAM policies for delete permission on the data and metadata prefixes.
Example fix
// before: overlapping expire jobs
spark.submit(expireJob); anotherClient.expireSnapshots(table);
// after: serialize cleanup runs
if (lockAcquired(table)) {
SparkActions.get().expireSnapshots(table).olderThan(ts).execute();
} Defensive patterns
Strategy: retry
Try / catch
try { actions.expireSnapshots(table).olderThan(ts).execute(); } catch (RuntimeException e) { /* re-run; deletes are idempotent */ } Prevention
- Run a single expireSnapshots job per table at a time
- Throttle/rate-limit cleanup to avoid object-store throttling
- Re-run expiration after partial bulk-delete failures
- Verify delete permissions on data and metadata prefixes
When it happens
Trigger: expireSnapshots (or similar cleanup) with a bulk-capable FileIO; the object store's bulk delete fails for some objects — throttling, permissions on individual keys, objects concurrently deleted by another cleanup job, or batch failures in the S3/GCS client.
Common situations: Snapshot expiration under aggressive S3 rate limits; overlapping expireSnapshots jobs from different clients; bucket policies denying deletes for certain prefixes; large batches exceeding provider batch limits.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- An error occurred closing catalog instance, ignoring...
- An error occurred while aborting the stream
- Bulk deletion failed
- Cannot clean files incrementally when snapshot IDs are…
- Cannot expire snapshots from a
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/a461ead8315c15a0.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/FileCleanupStrategy.java:112
protected CloseableIterable<ManifestFile> readManifests(Snapshot snapshot) {
if (snapshot.manifestListLocation() != null) {
return InternalData.read(
FileFormat.AVRO, fileIO.newInputFile(snapshot.manifestListLocation()))
.setRootType(GenericManifestFile.class)
.project(MANIFEST_PROJECTION)
.reuseContainers()
.build();
} else {
return CloseableIterable.withNoopClose(snapshot.allManifests(fileIO));
}
}
protected void deleteFiles(Set<String> pathsToDelete, String fileType) {
if (deleteFunc == null && fileIO instanceof SupportsBulkOperations) {
try {
((SupportsBulkOperations) fileIO).deleteFiles(pathsToDelete);
} catch (BulkDeletionFailureException e) {
LOG.warn(
"Bulk deletion failed for {} of {} {} file(s)",
e.numberFailedObjects(),
pathsToDelete.size(),
fileType,
e);
} catch (RuntimeException e) {
LOG.warn("Bulk deletion failed", e);
}
} else {
Consumer<String> deleteFuncToUse = deleteFunc == null ? defaultDeleteFunc : deleteFunc;
Tasks.foreach(pathsToDelete)
.executeWith(deleteExecutorService)
.retry(3)
.stopRetryOn(NotFoundException.class)
.stopOnFailure()
.suppressFailureWhenFinished()
.onFailure(View on GitHub (pinned to 86d9c8fc54)