apache/flink · error · IOException

Stream closed.

Error message

Stream closed.

What it means

Thrown by RefCountedBufferingFileStream.requireOpen() when any operation (write/flush/getPos) is attempted on a stream whose `closed` flag is true. It is the standard 'use after close' guard for this buffering temp file stream.

Source

Thrown at flink-core/src/main/java/org/apache/flink/core/fs/RefCountedBufferingFileStream.java:141

        if (!closed) {
            currentTmpFile.closeStream();
            closed = true;
        }
    }

    @Override
    public void retain() {
        currentTmpFile.retain();
    }

    @Override
    public boolean release() {
        return currentTmpFile.release();
    }

    private void requireOpen() throws IOException {
        if (closed) {
            throw new IOException("Stream closed.");
        }
    }

    @Override
    public String toString() {
        return "Reference Counted File with {"
                + "path=\'"
                + currentTmpFile.getFile().toPath().toAbsolutePath()
                + "\'"
                + ", size="
                + getPos()
                + ", reference counter="
                + currentTmpFile.getReferenceCounter()
                + ", closed="
                + closed
                + '}';
    }

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Track ownership: ensure only one code path closes the stream, and no writes follow it.
  2. Reset the reference to null after close and null-check before use.
  3. Use try-with-resources or a lifecycle wrapper that guarantees write/close ordering.
  4. In failure handlers, set a flag and skip subsequent write/flush operations.

Example fix

// before
stream.write(buffer);
stream.close();
stream.flush(); // throws

// after
stream.write(buffer);
stream.close();
// no further operations; or guard:
if (!stream.isClosed()) { stream.flush(); }
Defensive patterns

Strategy: validation

Validate before calling

void safeFlush(RefCountedBufferingFileStream s) throws IOException {
    if (s.isClosed()) return; // or throw IllegalStateException
    s.flush();
}

Try / catch

try {
    stream.flush();
} catch (IOException e) {
    if ("Stream closed.".equals(e.getMessage())) { /* already closed, ignore */ return; }
    throw e;
}

Prevention

When it happens

Trigger: Calling write(), flush(), or getPos() on a RefCountedBufferingFileStream after close() has been invoked; double-close paired with a subsequent operation; an exception in one branch leaving the stream closed while another code path keeps writing.

Common situations: Sink writer cleanup paths; concurrent close+write during task failure/cancellation; error-handling code that closes on failure but a finally block still attempts to flush.

Related errors


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