apache/iceberg · warning
Unclosed S3FileIO instance created by:\n\t
Error message
Unclosed S3FileIO instance created by:\n\t{} What it means
S3FileIO.finalize() detects FileIO instances that were never close()d before garbage collection. It closes the resource, then logs this warning with the stack trace captured at creation time so developers can find the leak. Unclosed S3FileIO instances leak executor threads and credential-refresh schedulers.
Solutions
- Close S3FileIO in try-with-resources or a finally block
- Reuse a single S3FileIO instance across operations instead of creating per call
- Use the logged create-stack trace to locate the leaking construction site
- Ensure catalog-managed FileIO instances are closed when the table/catalog is discarded
Example fix
// before
FileIO io = new S3FileIO();
io.deleteFile(path); // never closed
// after
try (FileIO io = new S3FileIO()) {
io.deleteFile(path);
} Defensive patterns
Strategy: type-guard
Validate before calling
// assert closure at end of job lifecycle
if (!ioClosed) throw new IllegalStateException("S3FileIO must be closed before shutdown"); Try / catch
try (S3FileIO io = new S3FileIO()) { io.initialize(props); /* use io */ } Prevention
- Always use try-with-resources for S3FileIO
- Share one instance per job/table rather than instantiating per file
- Treat the create-stack in this warning as the definitive leak location
When it happens
Trigger: Application code creates S3FileIO (directly or via a catalog/loader) and never calls close(), then the instance becomes garbage.
Common situations: Per-file S3FileIO instantiation instead of sharing one; jobs that build Hadoop-aware FileIO without try-with-resources; leaked references in long-running services (Spark/Flink tasks).
Related errors
- Unclosed input stream created by:\n\t
- Failed to delete object at path
- Unclosed input stream created by
- Unclosed output stream created by:\n\t
- An error occurred while aborting the stream
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/d679827285b91228.
Report an issue: GitHub.
Appendix: source
Thrown at aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIO.java:562
}
if (refreshFuture != null) {
refreshFuture.cancel(true);
refreshFuture = null;
}
}
}
@SuppressWarnings({"checkstyle:NoFinalizer", "Finalize", "deprecation"})
@Override
protected void finalize() throws Throwable {
super.finalize();
if (!isResourceClosed.get()) {
close();
if (null != createStack) {
String trace =
Joiner.on("\n\t").join(Arrays.copyOfRange(createStack, 1, createStack.length));
LOG.warn("Unclosed S3FileIO instance created by:\n\t{}", trace);
}
}
}
@Override
public boolean recoverFile(String path) {
PrefixedS3Client client = clientForStoragePath(path);
S3URI location = new S3URI(path, client.s3FileIOProperties().bucketToAccessPointMapping());
ListObjectVersionsIterable response =
client
.s3()
.listObjectVersionsPaginator(
builder -> builder.bucket(location.bucket()).prefix(location.key()));
// Recover to the last modified version, not isLatest,
// since isLatest is true for deletion markers.
Optional<ObjectVersion> recoverVersion =
response.versions().stream().max(Comparator.comparing(ObjectVersion::lastModified));View on GitHub (pinned to 86d9c8fc54)