eclipse-vertx/vert.x · error · IllegalStateException
This operation must be called from a Vert.x thread
Error message
This operation must be called from a Vert.x thread
What it means
InboundBuffer methods such as write() must run on a Vert.x thread owned by the buffer's context; checkThread() enforces this by testing context.inThread(). The buffer mutates its pending queue and demand non-thread-safely, so off-thread calls would corrupt state, and Vert.x throws IllegalStateException instead.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/streams/impl/InboundBuffer.java:103
}
public InboundBuffer(Context context, long highWaterMark) {
if (context == null) {
throw new NullPointerException("context must not be null");
}
if (highWaterMark < 0) {
throw new IllegalArgumentException("highWaterMark " + highWaterMark + " >= 0");
}
this.context = (ContextInternal) context;
this.highWaterMark = highWaterMark;
this.demand = Long.MAX_VALUE;
// empty ArrayDeque's constructor ArrayDeque allocates 16 elements; let's delay the allocation to be of the proper size
this.pending = null;
}
private void checkThread() {
if (!context.inThread()) {
throw new IllegalStateException("This operation must be called from a Vert.x thread");
}
}
/**
* Write an {@code element} to the buffer. The element will be delivered synchronously to the handler when
* it is possible, otherwise it will be queued for later delivery.
*
* @param element the element to add
* @return {@code false} when the producer should stop writing
*/
public boolean write(E element) {
checkThread();
Handler<E> handler;
synchronized (this) {
if (demand == 0L || emitting) {
if (pending == null) {
pending = new ArrayDeque<>(1);
}View on GitHub (pinned to fb308bd8c3)
Solutions
- Wrap the write in the buffer's context: context.runOnContext(v -> inboundBuffer.write(element))
- Dispatch from the third-party callback via vertx.runOnContext or a handler invoked on a Vert.x thread
- If in a Worker/startThread scope, ensure the code executes on a context thread (vertx.executeBlocking with the right context)
Example fix
// before externalClient.onMessage(msg -> inboundBuffer.write(msg)); // after externalClient.onMessage(msg -> context.runOnContext(v -> inboundBuffer.write(msg)));
Defensive patterns
Strategy: try-catch
Validate before calling
if (ctx != null && !ctx.isRunningOnContext()) {
ctx.runOnContext(v -> inboundBuffer.write(element));
} else {
inboundBuffer.write(element);
} Try / catch
try {
inboundBuffer.write(element);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Vert.x thread")) {
context.runOnContext(v -> inboundBuffer.write(element));
} else throw e;
} Prevention
- Always bridge third-party callback threads with context.runOnContext
- Keep all ReadStream state mutation on the owning context
- Enable Vert.x assertion mode (-Dvertx.debug=true style checks) in tests to catch off-thread access early
When it happens
Trigger: Calling inboundBuffer.write(element) (or fetch/pause/resume/draining paths that delegate to write) from a plain application thread, a third-party callback thread (e.g. a JDBC or MQTT client thread), or a different Vert.x context's event-loop/worker thread than the one the buffer was created with.
Common situations: Writing into a ReadStream's buffer directly from a custom client's network thread; delivering results from an executor instead of ctx.runOnContext; accessing the stream after its context/worker shut down.
Related errors
- context must not be null
- blockedThreadCheckInterval must be > 0
- maxEventLoopExecuteTime must be > 0
- maxWorkerpExecuteTime must be > 0
- internalBlockingPoolSize must be > 0
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/35203b2158536edb.
Report an issue: GitHub.