quarkusio/quarkus · error · IOException

Stream is closed

Error message

Stream is closed

What it means

VertxClientInputStream.read throws IOException("Stream is closed") when the response body stream has already been closed and read() is called again. This is standard InputStream closed-stream semantics: once the client response (and its underlying Netty connection buffer) is released, further reads are illegal.

Source

Thrown at independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/VertxClientInputStream.java:49

    @Override
    public int read() throws IOException {
        byte[] b = new byte[1];
        int read = read(b);
        if (read == -1) {
            return -1;
        }
        return b[0] & 0xff;
    }

    @Override
    public int read(final byte[] b) throws IOException {
        return read(b, 0, b.length);
    }

    @Override
    public int read(final byte[] b, final int off, final int len) throws IOException {
        if (closed) {
            throw new IOException("Stream is closed");
        }
        readIntoBuffer();
        if (finished) {
            return -1;
        }
        if (len == 0) {
            return 0;
        }
        ByteBuf buffer = pooled;
        int copied = Math.min(len, buffer.readableBytes());
        buffer.readBytes(b, off, copied);
        if (!buffer.isReadable()) {
            pooled.release();
            pooled = null;
        }
        return copied;
    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Read the entity fully before closing the Response / leaving the try-with-resources block.
  2. Do not read the entity stream twice — buffer it (e.g. readAllBytes()) if the content is needed more than once.
  3. Avoid caching the InputStream beyond the response lifecycle; copy to a ByteArrayOutputStream if needed later.
  4. If the stream closes unexpectedly mid-read, check connection timeouts/idle settings on the REST client config.

Example fix

// before
byte[] data;
try (Response res = client.get()) {
  data = null;
}
data = res.readEntity(InputStream.class).readAllBytes(); // closed
// after
byte[] data;
try (Response res = client.get()) {
  data = res.readEntity(InputStream.class).readAllBytes();
}
Defensive patterns

Strategy: try-catch

Try / catch

byte[] data;
try (Response res = client.target(uri).request().get()) {
  try (InputStream in = res.readEntity(InputStream.class)) {
    data = in.readAllBytes();
  }
} catch (IOException e) {
  if ("Stream is closed".equals(e.getMessage())) {
    // re-issue the request; never reuse the cached stream
    data = reissueRequest();
  } else { throw new UncheckedIOException(e); }
}

Prevention

When it happens

Trigger: Calling read() on the Response.readEntity() stream after the response was closed, after the connection was returned to the pool, double-consuming the entity (read twice), or reading after try-with-resources closed the Response.

Common situations: Caching the InputStream and reading it later outside the request scope; closing the response in a finally block then using the stream; concurrent reads of the same response body from multiple threads; timeouts that close the connection mid-read.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/59756f55b2d62974. Report an issue: GitHub.