apache/beam · error · IOException

Stream has been finished. Can not add any more elements.

Error message

Stream has been finished. Can not add any more elements.

What it means

BufferedElementCountingOutputStream counts elements written and emits length markers; once finished() is called the stream is closed for further output. markElementStart() checks the finished flag and throws IOException if a new element is started after finish. The stream is a one-shot sink: finishing is terminal.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/util/BufferedElementCountingOutputStream.java:134

    if (finished) {
      return;
    }
    flush();
    // Finish the stream with the terminatorValue.
    VarInt.encode(terminatorValue, os);
    if (!BUFFER_POOL.offer(buffer)) {
      // The pool is full, we can't store the buffer. We just drop the buffer.
    }
    finished = true;
  }

  /**
   * Marks that a new element is being output. This allows this output stream to use the buffer if
   * it had previously overflowed marking the start of a new block of elements.
   */
  public void markElementStart() throws IOException {
    if (finished) {
      throw new IOException("Stream has been finished. Can not add any more elements.");
    }
    count++;
  }

  @Override
  public void write(int b) throws IOException {
    if (finished) {
      throw new IOException("Stream has been finished. Can not write any more data.");
    }
    if (count == 0) {
      os.write(b);
      return;
    }

    if (buffer.hasRemaining()) {
      buffer.put((byte) b);
    } else {
      outputBuffer();

View on GitHub (pinned to 12126d8942)

Solutions

  1. Create a new BufferedElementCountingOutputStream for each batch instead of reusing a finished instance.
  2. Ensure finish() is called only after all elements have been marked and written; move finish() to the true end of the encode loop.
  3. If reuse is required, track a 'finished' state in your wrapper and reinitialize the underlying stream before further writes.

Example fix

// before
stream.markElementStart(); writeElement(stream);
stream.finish();
stream.markElementStart(); // IOException
// after
stream.markElementStart(); writeElement(stream);
stream.finish();
stream = new BufferedElementCountingOutputStream(os, 20);
stream.markElementStart(); writeElement(stream);
Defensive patterns

Strategy: validation

Validate before calling

if (!stream.finished /* or your own flag */) { stream.markElementStart(); }

Try / catch

try (BufferedElementCountingOutputStream s = ...) {
  // write all elements
} // finish via close; never write afterwards

Prevention

When it happens

Trigger: Calling markElementStart() after finish() — e.g. a caller keeps encoding elements after closing, or reuses the same output-stream wrapper for a second batch without creating a new instance.

Common situations: Reuse of a stream across batches or retries after finish; encode() loops whose termination condition runs one iteration past finish; custom sinks built on this stream that flush-then-finish but still receive data.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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