apache/beam · error · ClosedChannelException

null

Error message

null

What it means

AzureReadableSeekableByteChannel.read() throws ClosedChannelException when the channel has already been closed. The reported message is null because ClosedChannelException is constructed without a message. Any read attempt after close() (or use of a channel closed by an error path) fails this way.

Solutions

  1. Keep all read calls inside the try-with-resources scope that owns the channel.
  2. Reopen the channel with FileSystems.open() if you need to read after closing.
  3. Check !channel.isOpen() before reading, or restructure so each read pass uses a fresh channel.
  4. Synchronize close/read across threads so reads never race a close.

Example fix

// before
ReadableByteChannel ch = FileSystems.open(resourceId);
ch.close();
ch.read(buffer); // ClosedChannelException
// after
try (ReadableByteChannel ch = FileSystems.open(resourceId)) {
  ch.read(buffer);
}
Defensive patterns

Strategy: try-catch

Type guard

boolean readable(SeekableByteChannel ch) { return ch != null && ch.isOpen(); }

Try / catch

try (ReadableByteChannel ch = FileSystems.open(resourceId)) {
  ch.read(buffer);
} catch (ClosedChannelException e) {
  // reopen and retry once
  try (ReadableByteChannel ch2 = FileSystems.open(resourceId)) { ch2.read(buffer); }
}

Prevention

When it happens

Trigger: Calling channel.read(buffer) after channel.close() was invoked; continuing to read after a try-with-resources block has exited; closing in one thread while another thread still reads.

Common situations: Reusing a reader object outside the scope of the try-with-resources that created it; wrapping the channel in code that closes it eagerly (e.g. after count-limited reads); concurrent close in multi-threaded pipelines.

Related errors


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

Appendix: source

Thrown at sdks/java/io/azure/src/main/java/org/apache/beam/sdk/io/azure/blobstore/AzureReadableSeekableByteChannel.java:46

class AzureReadableSeekableByteChannel implements SeekableByteChannel {

  private final BlobInputStream inputStream;
  private boolean closed;
  private final Long contentLength;
  private long position = 0;

  public AzureReadableSeekableByteChannel(BlobClient blobClient) {
    inputStream = blobClient.openInputStream();
    contentLength = blobClient.getProperties().getBlobSize();
    inputStream.mark(contentLength.intValue());
    closed = false;
  }

  @Override
  public int read(ByteBuffer dst) throws IOException {
    if (closed) {
      throw new ClosedChannelException();
    }

    int read = 0;
    if (dst.hasArray()) {
      // Stores up to dst.remaining() bytes into dst.array() starting at dst.position().
      // But dst can have an offset with its backing array, hence the + dst.arrayOffset().
      read = inputStream.read(dst.array(), dst.position() + dst.arrayOffset(), dst.remaining());
    } else {
      byte[] myarray = new byte[dst.remaining()];
      read = inputStream.read(myarray, 0, myarray.length);
      dst.put(myarray);
    }

    if (read > 0) {
      dst.position(dst.position() + read);
    }
    return read;
  }

View on GitHub (pinned to 12126d8942)