quarkusio/quarkus · error · IOException
Stream is closed
Error message
Stream is closed
What it means
read() was called on a VertxInputStream after the request body stream was closed. Quarkus wraps the Vert.x request body in this InputStream; once closed=true (explicit close() or connection teardown) any further read throws IOException("Stream is closed").
Source
Thrown at extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java:79
@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");
}
if (continueState == ContinueState.REQUIRED) {
continueState = ContinueState.SENT;
exchange.request.response().writeContinue();
}
readIntoBuffer();
if (limit > 0 && exchange.request.bytesRead() > limit) {
HttpServerResponse response = exchange.request.response();
if (response.headWritten()) {
//the response has been written, not much we can do
exchange.request.connection().close();
throw new IOException("Request too large");
} else {
response.setStatusCode(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE.code());
response.headers().add(HttpHeaderNames.CONNECTION, "close");
response.endHandler(new Handler<Void>() {
@Override
public void handle(Void event) {View on GitHub (pinned to e1c734241f)
Solutions
- Read the request body exactly once, before any close, and buffer it if it must be consumed more than once.
- Check application code for accidental close() of the request InputStream (closing it is not required and poisons further reads).
- Ensure the body is fully read during the blocking request phase, not after the response has been sent or the request dispatched.
- If caching is needed, wrap with a duplicate/caching filter or use quarkus.http.body-related caching facilities instead of re-reading the raw stream.
Example fix
// before
try (InputStream in = request.getInputStream()) {
body = in.readAllBytes();
} // stream closed; later code calls in.read() -> 'Stream is closed'
// after
InputStream in = request.getInputStream();
byte[] body = in.readAllBytes(); // do NOT close the request stream; reuse bytes as needed Defensive patterns
Strategy: validation
Validate before calling
if (in == null) throw new IllegalStateException("no request stream");
// read body once and cache before any close()
byte[] cached = in.readAllBytes(); Try / catch
try {
int n = in.read(buf);
} catch (IOException e) {
if ("Stream is closed".equals(e.getMessage())) {
// treat as end-of-stream: use previously cached body
} else throw e;
} Prevention
- Never call close() on the request InputStream
- Read the body once and cache bytes if multiple consumers need it
- Consume the body before writing the response or completing async work
When it happens
Trigger: Calling read()/read(byte[],off,len) on the request InputStream after close() was invoked, or after the request/response lifecycle already ended (e.g. reading the body after writing the response or after async dispatch completed).
Common situations: Filters or interceptors that close the input stream then downstream code tries to read the body; reading the body twice (e.g. once for logging, once for deserialization) without caching; asynchronous processing that outlives the request.
Related errors
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/bd54a240d9015f41.
Report an issue: GitHub.