quarkusio/quarkus · error · BlockingOperationNotAllowedException
Attempting a blocking read on io thread
Error message
Attempting a blocking read on io thread
What it means
Blocking reads on the request InputStream are forbidden on the Vert.x event-loop (IO) thread. VertxInputStream.readBlocking checks Context.isOnEventLoopThread() and throws BlockingOperationNotAllowedException("Attempting a blocking read on io thread") to prevent freezing all request processing.
Source
Thrown at extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java:245
}
protected ByteBuf readBlocking() throws IOException {
long expire = System.currentTimeMillis() + timeout;
synchronized (request.connection()) {
while (input1 == null && !eof && readException == null) {
long rem = expire - System.currentTimeMillis();
if (rem <= 0) {
//everything is broken, if read has timed out we can assume that the underling connection
//is wrecked, so just close it
request.connection().close();
IOException throwable = new IOException("Read timed out");
readException = throwable;
throw throwable;
}
try {
if (Context.isOnEventLoopThread()) {
throw new BlockingOperationNotAllowedException("Attempting a blocking read on io thread");
}
waiting = true;
request.connection().wait(rem);
} catch (InterruptedException e) {
throw new InterruptedIOException(e.getMessage());
} finally {
waiting = false;
}
}
if (readException != null) {
throw new IOException(readException);
}
Buffer ret = input1;
input1 = null;
if (inputOverflow != null) {
input1 = inputOverflow.poll();
if (input1 == null) {
request.fetch(1);View on GitHub (pinned to e1c734241f)
Solutions
- Annotate the endpoint or filter @Blocking, or use @RunOnVirtualThread, so the handler runs on a worker thread.
- Read the request body using the reactive API (e.g. route body handler / Multi<Buffer>) instead of the blocking InputStream.
- In filters/interceptors, dispatch to a worker executor before consuming the body.
- Avoid manual InputStream reads inside Vert.x handlers; let the framework buffer the body first.
Example fix
// before
@POST
public String upload() throws IOException { // runs on event-loop
return new String(request.getInputStream().readAllBytes());
}
// after
@POST
@RunOnVirtualThread // or @Blocking
public String upload() throws IOException {
return new String(request.getInputStream().readAllBytes());
} Defensive patterns
Strategy: try-catch
Validate before calling
if (io.vertx.core.Context.isOnEventLoopThread()) {
throw new IllegalStateException("Body must be read on a worker thread: use @Blocking / @RunOnVirtualThread or the reactive body API");
} Try / catch
try {
in.read(buf);
} catch (BlockingOperationNotAllowedException e) {
// re-dispatch to worker executor and retry the read there
workerExecutor.executeBlocking(() -> readBody(in));
} Prevention
- Annotate body-consuming endpoints @Blocking or @RunOnVirtualThread
- Prefer reactive body handlers (Multi<Buffer>) on event-loop code
- Never read InputStreams inside Vert.x handlers
When it happens
Trigger: Calling in.read() (which waits via request.connection().wait) on the request body from code running on an event-loop thread — e.g. inside a non-blocking route handler, a Vert.x handler, or a filter not marked @Blocking.
Common situations: RESTEasy Reactive / Vert.x route handlers that consume the raw InputStream directly; Quarkus Security or filter code reading the body on the IO thread; migrating servlet-style blocking code into reactive endpoints without @Blocking/@RunOnVirtualThread.
Related errors
- Blocking gRPC client call made from the event loop. If the c
- You have attempted to perform a blocking operation on a IO t
- @Transactional cannot start a JTA transaction within a react
- You have attempted to inject AuthzClient on a IO thread. Thi
- No current Vertx context found
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/310cf5de5461e44e.
Report an issue: GitHub.