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

  1. Wrap the OutputStream from FileIO.create(...) in try-with-resources so close() is always called, even on exception.
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/72d409aa03e42fe1. Report an issue: GitHub.