{"record":{"id":"d7b928ddd79b70ad","repo":"quarkusio/quarkus","slug":"attempting-a-blocking-read-on-io-thread-d7b928","errorCode":null,"errorMessage":"Attempting a blocking read on io thread","messagePattern":"Attempting a blocking read on io thread","errorType":"exception","errorClass":"BlockingNotAllowedException","httpStatus":null,"severity":"error","filePath":"independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/VertxClientInputStream.java","lineNumber":181,"sourceCode":"        }\n\n        protected ByteBuf readBlocking() throws IOException {\n            long expire = System.currentTimeMillis() + timeout;\n            synchronized (VertxBlockingInput.this) {\n                while (input1 == null && !eof && readException == null) {\n                    long rem = expire - System.currentTimeMillis();\n                    if (rem <= 0) {\n                        //everything is broken, if read has timed out we can assume that the underling connection\n                        //is wrecked, so just close it\n                        request.netSocket().close();\n                        IOException throwable = new IOException(\"Read timed out\");\n                        readException = throwable;\n                        throw throwable;\n                    }\n\n                    try {\n                        if (Context.isOnEventLoopThread()) {\n                            throw new BlockingNotAllowedException(\"Attempting a blocking read on io thread\");\n                        }\n                        waiting = true;\n                        VertxBlockingInput.this.wait(rem);\n                    } catch (InterruptedException e) {\n                        throw new InterruptedIOException(e.getMessage());\n                    } finally {\n                        waiting = false;\n                    }\n                }\n                if (readException != null) {\n                    throw new IOException(readException);\n                }\n                Buffer ret = input1;\n                input1 = null;\n                if (inputOverflow != null) {\n                    input1 = inputOverflow.poll();\n                    if (input1 == null) {\n                        request.fetch(1);","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/VertxClientInputStream.java#L163-L199","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move the blocking read onto a worker thread: annotate the handler/method @Blocking (Quarkus) or dispatch to an executor","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","Use Response.bufferEntity()/readEntity(Class) with the async invocation so the body is aggregated without blocking the caller","Never call blocking IO directly from Context.isOnEventLoopThread()==true code paths"],"exampleFix":"// before (event-loop thread)\n@GET\npublic String proxy() {\n    return client.target(url).request().get().readEntity(String.class); // BlockingNotAllowedException\n}\n\n// after\n@GET\n@Blocking\npublic String proxy() {\n    return client.target(url).request().get().readEntity(String.class);\n}","handlingStrategy":"validation","validationCode":"if (io.vertx.core.Context.isOnEventLoopThread()) {\n    throw new IllegalStateException(\"Do not block-read entity stream on event loop thread; dispatch to worker\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return stream.readAllBytes();\n} catch (BlockingNotAllowedException e) {\n    // re-dispatch to a worker thread/executor and retry there\n    return executeOnWorker(() -> readAll(stream));\n}","preventionTips":["Annotate blocking handlers with @Blocking (Quarkus) or dispatch to a worker executor","Prefer the async client API (Uni/CompletionStage) when on the event loop","Never call blocking InputStream reads inside reactive callbacks, filters, or event-loop handlers","Check Context.isOnEventLoopThread() in shared utility code before blocking reads"],"tags":["blocking","event-loop","vertx","rest-client","concurrency"],"backgroundTag":"blocking-call-on-event-loop","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}