apache/flink · error · IOException

Illegal attempt to write to closed output stream

Error message

Illegal attempt to write to closed output stream

What it means

GSRecoverableFsDataOutputStream.write throws this IOException when bytes are written after the stream has already been closed. The GCS recoverable output stream is single-use: once close() (or closeForCommit()) has run, the underlying write channel state is final and any further write is a programming error.

Source

Thrown at flink-filesystems/flink-gs-fs-hadoop/src/main/java/org/apache/flink/fs/gs/writer/GSRecoverableFsDataOutputStream.java:146

        write(bytes);
    }

    @Override
    public void write(@Nonnull byte[] content) throws IOException {
        Preconditions.checkNotNull(content);

        write(content, 0, content.length);
    }

    @Override
    public void write(@Nonnull byte[] content, int start, int length) throws IOException {
        Preconditions.checkNotNull(content);
        Preconditions.checkArgument(start >= 0);
        Preconditions.checkArgument(length >= 0);

        // if the data stream is already closed, throw an exception
        if (closed) {
            throw new IOException("Illegal attempt to write to closed output stream");
        }

        // if necessary, create a write channel
        if (currentWriteChannel == null) {
            LOGGER.debug("Creating write channel for blob {}", finalBlobIdentifier);
            currentWriteChannel = createWriteChannel();
        }

        // write to the stream. the docs say that, in some circumstances, though an attempt will be
        // made to write all of the requested bytes, there are some cases where only some bytes will
        // be written. it's not clear whether this could ever happen with a Google storage
        // WriteChannel; in any case, recoverable writers don't support partial writes, so if this
        // ever happens, we must fail the write.:
        // https://docs.oracle.com/javase/7/docs/api/java/nio/channels/WritableByteChannel.html#write(java.nio.ByteBuffer)
        LOGGER.trace("Writing {} bytes", length);
        int bytesWritten = currentWriteChannel.write(content, start, length);
        if (bytesWritten != length) {
            throw new IOException(

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Audit call sites to guarantee no write occurs after close()/closeForCommit(); track stream lifecycle explicitly
  2. Open a fresh stream with GSRecoverableWriter.open(path) or resume via recover(writer, resumeRecoverable) instead of writing to the closed instance
  3. If resuming after a failure, use the GSResumeRecoverable from keep() / persist(), never the closed stream object

Example fix

// before
stream.write(data, 0, data.length); // may run after close() in finally
// after
if (!streamClosed) {
    stream.write(data, 0, data.length);
}
Defensive patterns

Strategy: validation

Validate before calling

// track lifecycle yourself; the stream exposes no isOpen() check
if (!streamClosed) {
    stream.write(content, 0, content.length);
}

Try / catch

try {
    stream.write(content, 0, content.length);
} catch (IOException e) {
    if (e.getMessage().contains("closed output stream")) {
        // bug in caller lifecycle: open a new stream via writer.open(), do not retry this one
    }
}

Prevention

When it happens

Trigger: Calling write(byte[], int, int) (or write(byte[])) on a GSRecoverableFsDataOutputStream instance after close() or closeForCommit() was already invoked on that instance; e.g. a sink flushing buffers in a finally block after an earlier close, or two code paths closing and then reusing the same stream.

Common situations: Custom sink implementations that retry a failed write after the stream was closed; error-handling paths that close the stream and then attempt a final flush/write; reusing a stream reference across task restarts or recovery attempts instead of opening a new one via GSRecoverableWriter.open().

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/2d6f59a4e6c8224f. Report an issue: GitHub.