apache/iceberg · warning
Unclosed output stream created by: {}
Error message
Unclosed output stream created by:
{} What it means
This is a diagnostic warning (not an exception thrown to the caller) emitted from OSSOutputStream.finalize() when the garbage collector reclaims an output stream that was never closed. Iceberg detects the leak via the 'closed' flag and closes the stream defensively to release the underlying OSS resources, while logging the stack trace of where the stream was created so the developer can fix the missing close().
Source
Thrown at aliyun/src/main/java/org/apache/iceberg/aliyun/oss/OSSOutputStream.java:174
PutObjectRequest request =
new PutObjectRequest(uri.bucket(), uri.key(), contentStream, metadata);
client.putObject(request);
}
private void cleanUpStagingFiles() {
if (!currentStagingFile.delete()) {
LOG.warn("Failed to delete staging file: {}", currentStagingFile);
}
}
@SuppressWarnings({"checkstyle:NoFinalizer", "Finalize", "deprecation"})
@Override
protected void finalize() throws Throwable {
super.finalize();
if (!closed) {
close(); // releasing resources is more important than printing the warning.
String trace = Joiner.on("\n\t").join(Arrays.copyOfRange(createStack, 1, createStack.length));
LOG.warn("Unclosed output stream created by:\n\t{}", trace);
}
}
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Wrap the OutputStream from FileIO.create(...) in try-with-resources so close() is always called, even on exception.
- Audit the code path identified by the logged 'created by' stack trace and add explicit close() in a finally block where try-with-resources is not possible.
- If the warning appears under load, check writer/task cleanup in your engine integration (e.g. task abort logic) to ensure writers are closed on failure.
- Reproduce with a smaller workload and monitor GC logs to confirm no streams are being dropped.
Example fix
// before
OutputStream out = io.newOutputFile(path).create();
out.write(data);
// after
try (OutputStream out = io.newOutputFile(path).create()) {
out.write(data);
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure every OSS/FileIO output stream is closed; prefer try-with-resources.
// Pre-flight check in code review: search for '.create()' on OutputFile and confirm each is in try-with-resources.
try (OutputStream out = io.newOutputFile(path).create()) {
// write
} Prevention
- Always use try-with-resources for OutputStreams from FileIO/OSSOutputFile.
- Never store OutputStreams in fields without a close lifecycle (implement AutoCloseable on owning classes).
- In task-abort/error paths, explicitly close all opened writers.
- Treat 'Unclosed output stream' warnings as actionable: follow the logged creation stack trace.
When it happens
Trigger: An OSSOutputStream created by the Aliyun OSS FileIO write path was garbage collected without close() ever being called — e.g. a caller obtained an OutputStream from OSSOutputFile.create() and dropped it without closing, or an exception was thrown mid-write and no try-with-resources/finally cleanup ran.
Common situations: Writing a data/metadata file to OSS with an unclosed stream during an error path; forgetting try-with-resources around the OutputStream returned by a FileIO write; frameworks that abort a task without closing task-writer streams; long-running Spark/Flink jobs where leaked streams accumulate until GC triggers the warning.
Related errors
- Unclosed input stream created by: {}
- Failed to create RRSA OSS client
- Location already exists: %s
- Unable to load metrics class: '{}', falling back to null met
- Failed to delete staging file: {}
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/72d409aa03e42fe1.
Report an issue: GitHub.