quarkusio/quarkus · error · IllegalStateException

MultiByteHttpData invoked on an invalid context :

Error message

MultiByteHttpData invoked on an invalid context : 

What it means

MultiByteHttpData mutates a non-thread-safe ByteBuf and drives a Vert.x-context-bound subscription, so getChunk(int) may only be called on the exact Vert.x Context captured at construction. getChunk verifies Vertx.currentContext() == context and throws IllegalStateException when invoked from a different context or thread (the message prints the offending context and thread).

Source

Thrown at independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/MultiByteHttpData.java:197

     * @return true if the requested amount of bytes is ready to be read or the Multi is completed, i.e. there will be
     *         no more bytes to read
     */
    public boolean isReady(int chunkSize) {
        return done || buffer.readableBytes() >= chunkSize;
    }

    /**
     * {@inheritDoc}
     * <br/>
     * NOTE: should only be invoked when {@link #isReady(int)} returns true
     *
     * @param toRead amount of bytes to read
     * @return ByteBuf with the requested bytes
     */
    @Override
    public ByteBuf getChunk(int toRead) {
        if (Vertx.currentContext() != context) {
            throw new IllegalStateException("MultiByteHttpData invoked on an invalid context : " + Vertx.currentContext()
                    + ", thread: " + Thread.currentThread());
        }
        if (buffer.readableBytes() == 0 && done) {
            return Unpooled.EMPTY_BUFFER;
        }

        ByteBuf result = VertxByteBufAllocator.DEFAULT.heapBuffer(toRead, toRead);

        // finish if the whole buffer is filled
        // or we hit the end, `done` && buffer.readableBytes == 0
        while (toRead > 0 && !(buffer.readableBytes() == 0 && done)) {
            int readBytes = Math.min(buffer.readableBytes(), toRead);
            result.writeBytes(buffer.readBytes(readBytes));
            buffer.discardReadBytes();
            subscription.request(readBytes);

            toRead -= readBytes;
        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure encoding/read of chunks happens on the same Vert.x context that created the part - submit work via context.runOnContext(...) or the client's ExecutorWithContext.
  2. Do not call getChunk from arbitrary threads; route through PausableHttpPostRequestEncoder, which preserves the context.
  3. In tests, run getChunk inside Vertx.testContext().runOnContext(...) or capture the exact context used by the client.
  4. If you implemented custom resumption logic, make sure resumption.run() executes on the captured context (it is invoked from the Multi emissions, which are context-bound via ExecutorWithContext).

Example fix

// before
byte[] data = readChunkFromWorkerThread(httpData); // calls getChunk off-context
// after
context.runOnContext(v -> {
    if (httpData.isReady(chunkSize)) {
        ByteBuf chunk = httpData.getChunk(chunkSize);
        // ... use chunk here
    }
});
Defensive patterns

Strategy: validation

Validate before calling

if (Vertx.currentContext() != partContext) {
    throw new IllegalStateException("getChunk must run on the Vert.x context that created the part");
}
httpData.getChunk(toRead);

Type guard

boolean onValidContext(MultiByteHttpData data) {
    return Vertx.currentContext() == data.getCurrentContextOrNull(); // only if exposed; otherwise capture the context at part creation
}

Try / catch

try {
    httpData.getChunk(toRead);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("MultiByteHttpData invoked on an invalid context")) {
        // resubmit the read on the captured part context via context.runOnContext(...)
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling getChunk(int) from a thread whose Vert.x currentContext() differs from the context passed to the MultiByteHttpData constructor - e.g. from a plain worker thread, a Mutiny infrastructure thread, or a different Vert.x context than the one running the client request.

Common situations: Custom or wrapped post encoders that encode the multipart body on their own executor; moving encoding work off the IO thread; unit tests invoking getChunk directly from the test thread; callbacks that hop contexts before draining part data.

Related errors


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