quarkusio/quarkus · error · BlockingNotAllowedException

Attempting a blocking read on io thread

Error message

Attempting a blocking read on io thread

What it means

A blocking read on the response entity stream was attempted from a Vert.x event-loop (IO) thread. VertxBlockingInput.readBlocking() must park the calling thread with Object.wait() until data arrives; parking the event loop would freeze all IO for that thread, so the client refuses with BlockingNotAllowedException.

Source

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

        }

        protected ByteBuf readBlocking() throws IOException {
            long expire = System.currentTimeMillis() + timeout;
            synchronized (VertxBlockingInput.this) {
                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.netSocket().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;
                        VertxBlockingInput.this.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. Move the blocking read onto a worker thread: annotate the handler/method @Blocking (Quarkus) or dispatch to an executor
  2. Consume the entity fully on a worker thread before returning to the event loop, or use the async REST client API (CompletionStage/Uni) instead of a raw InputStream
  3. Use Response.bufferEntity()/readEntity(Class) with the async invocation so the body is aggregated without blocking the caller
  4. Never call blocking IO directly from Context.isOnEventLoopThread()==true code paths

Example fix

// before (event-loop thread)
@GET
public String proxy() {
    return client.target(url).request().get().readEntity(String.class); // BlockingNotAllowedException
}

// after
@GET
@Blocking
public String proxy() {
    return client.target(url).request().get().readEntity(String.class);
}
Defensive patterns

Strategy: validation

Validate before calling

if (io.vertx.core.Context.isOnEventLoopThread()) {
    throw new IllegalStateException("Do not block-read entity stream on event loop thread; dispatch to worker");
}

Try / catch

try {
    return stream.readAllBytes();
} catch (BlockingNotAllowedException e) {
    // re-dispatch to a worker thread/executor and retry there
    return executeOnWorker(() -> readAll(stream));
}

Prevention

When it happens

Trigger: Calling InputStream.read() (which reaches readBlocking()) on the entity stream from code running on an event-loop thread — e.g. inside a reactive route handler, a Vert.x Worker-less handler, an async filter, or onMessage-style callbacks.

Common situations: Mixing reactive and blocking styles: reading the response entity directly inside a Quarkus reactive/IO request handler, in a Vert.x verticle event-loop method, or in a non-blocking filter/interceptor; Quarkus routes annotated without @Blocking but calling blocking client code.

Related errors


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