apache/beam · error · UnsupportedOperationException

Caller does not own the underlying output stream and should

Error message

Caller does not own the underlying output stream  and should not call close().

What it means

UnownedOutputStream wraps an OutputStream the caller does not own; close() is intentionally unsupported because closing would also close a stream shared with other code (e.g. a file channel managed by Beam). The wrapper forwards writes but delegates lifecycle management to the owner.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/util/UnownedOutputStream.java:39

import java.io.IOException;
import java.io.OutputStream;
import org.apache.beam.sdk.annotations.Internal;
import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * A {@link OutputStream} wrapper which protects against the user attempting to modify the
 * underlying stream by closing it.
 */
@Internal
public class UnownedOutputStream extends FilterOutputStream {
  public UnownedOutputStream(OutputStream delegate) {
    super(delegate);
  }

  @Override
  public void close() throws IOException {
    throw new UnsupportedOperationException(
        "Caller does not own the underlying output stream " + " and should not call close().");
  }

  @Override
  public boolean equals(@Nullable Object obj) {
    return obj instanceof UnownedOutputStream && ((UnownedOutputStream) obj).out.equals(out);
  }

  @Override
  public int hashCode() {
    return out.hashCode();
  }

  @Override
  public String toString() {
    return MoreObjects.toStringHelper(UnownedOutputStream.class).add("out", out).toString();
  }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Do not close the wrapper; close only the underlying stream you actually own, after you are done writing
  2. Flush the wrapper explicitly instead of relying on close() to flush
  3. Avoid try-with-resources on the wrapper; manage the owner stream's lifecycle yourself
  4. If you must hand it to a close-happy API, pass your own stream and copy to the unowned one

Example fix

// before
try (OutputStream out = new UnownedOutputStream(rawOut)) {
  writeData(out);
}
// after
OutputStream out = new UnownedOutputStream(rawOut);
writeData(out);
out.flush();
// close rawOut only when you own it
Defensive patterns

Strategy: try-catch

Validate before calling

if (out instanceof UnownedOutputStream) { /* do not close; flush only */ }

Type guard

static boolean closable(OutputStream o) { return !(o instanceof UnownedOutputStream); }

Try / catch

try { out.close(); } catch (UnsupportedOperationException e) { // ownership violation: close only the underlying owner stream }

Prevention

When it happens

Trigger: Calling close() on a UnownedInputStream-style wrapper (e.g. channels.newOutputStream(...) wrapped streams returned by Beam APIs) or using try-with-resources on such a stream, whose implicit close() triggers the exception.

Common situations: try-with-resources around an output stream obtained from a Beam file/channel API; utility code that always closes OutputStream arguments; nested stream chains where an outer wrapper closes inner streams.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/7fa1b6c93ee66a23. Report an issue: GitHub.