quarkusio/quarkus · error · BlockingNotAllowedException

Attempting a blocking read on io thread

Error message

Attempting a blocking read on io thread

What it means

readBlocking() throws BlockingNotAllowedException('Attempting a blocking read on io thread') when the request body is not yet buffered and the current thread is a Vert.x event-loop thread. Blocking waits on the event loop would stall all other requests on that thread, so the framework refuses it.

Source

Thrown at independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxInputStream.java:247

        }

        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 BlockingNotAllowedException("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

  1. Annotate the endpoint/handler with @Blocking (Jakarta REST) or move the read to a worker thread/executUserBlocking code.
  2. Consume the request body reactively instead (RoutingContext.body() / multipart data) so no blocking wait is needed.
  3. Ensure the body is fully buffered before switching to event-loop code that reads the stream.

Example fix

// before
@GET @Path("/upload")
public String read(InputStream is) { is.read(); } // runs on event loop -> exception
// after
@GET @Path("/upload")
@Blocking
public String read(InputStream is) { is.read(); } // worker thread, blocking allowed
Defensive patterns

Strategy: validation

Validate before calling

if (io.vertx.core.Context.isOnEventLoopThread()) {
    throw new IllegalStateException("Move body reads to a worker/@Blocking thread");
}
stream.read(buf);

Try / catch

try {
    stream.read(buf);
} catch (org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException e) {
    // reschedule read on a worker executor
}

Prevention

When it happens

Trigger: Reading the request InputStream (directly or via blocking APIs like a Jakarta REST blocking endpoint that actually runs on the event loop) when the body hasn't fully arrived and Context.isOnEventLoopThread() is true.

Common situations: Calling blocking body reads inside non-blocking REST methods, Vert.x route handlers, or observers running on the event loop; misconfigured @Blocking annotation; using InputStream in a reactive handler.

Related errors


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