eclipse-vertx/vert.x · error · IllegalStateException
Cannot be called on a Vert.x worker thread / Cannot be calle
Error message
Cannot be called on a Vert.x worker thread / Cannot be called on a Vert.x event-loop thread
What it means
executeBlocking-style worker calls must not be made from Vert.x worker or event-loop threads (you would deadlock or violate the threading model). WorkerExecutor.unwrapWorkerExecutor throws IllegalStateException with a message identifying which thread kind was violated: worker thread or event-loop thread. Only non-Vert.x threads or virtual-thread contexts may obtain the executor this way.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/impl/WorkerExecutor.java:33
import io.vertx.core.internal.WorkerPool;
import io.vertx.core.spi.metrics.PoolMetrics;
import java.util.concurrent.CountDownLatch;
/**
* Execute events on a worker pool.
*
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class WorkerExecutor implements EventExecutor {
public static io.vertx.core.impl.WorkerExecutor unwrapWorkerExecutor() {
Thread thread = Thread.currentThread();
if (thread instanceof VertxThread) {
VertxThread vertxThread = (VertxThread) thread;
String msg = vertxThread.isWorker() ? "Cannot be called on a Vert.x worker thread" :
"Cannot be called on a Vert.x event-loop thread";
throw new IllegalStateException(msg);
}
ContextInternal ctx = VertxImpl.currentContext(thread);
if (ctx != null && ctx.inThread()) {
// It can only be a Vert.x virtual thread
return (io.vertx.core.impl.WorkerExecutor) ctx.executor();
} else {
return null;
}
}
private final WorkerPool workerPool;
private final WorkerTaskQueue orderedTasks;
private final ThreadLocal<Boolean> inThread = new ThreadLocal<>();
public WorkerExecutor(WorkerPool workerPool, WorkerTaskQueue orderedTasks) {
this.workerPool = workerPool;
this.orderedTasks = orderedTasks;
}View on GitHub (pinned to fb308bd8c3)
Solutions
- Do not call executeBlocking/WorkerExecutor from Vert.x worker or event-loop threads — run the work directly or restructure so the blocking call happens on a non-Vert.x thread
- If you need to run blocking work from a handler, capture the WorkerExecutor outside and schedule appropriately, or use context.executeBlocking which handles dispatch
- For nested blocking logic, await completion via futures rather than nesting worker calls
Example fix
// before (inside a verticle handler running on event loop)
WorkerExecutor exec = WorkerExecutor.unwrapWorkerExecutor(); // IllegalStateException
// after
WorkerExecutor exec = vertx.createSharedWorkerExecutor("my-worker");
vertx.executeBlocking(p -> { blockingWork(); p.complete(); }, false, res -> { ... }); Defensive patterns
Strategy: type-guard
Validate before calling
if (Thread.currentThread() instanceof VertxThread) {
throw new IllegalStateException("Do not call worker executor APIs from Vert.x threads");
} Type guard
boolean safeToExecuteBlocking() {
return !(Thread.currentThread() instanceof VertxThread);
} Try / catch
try {
WorkerExecutor.unwrapWorkerExecutor();
} catch (IllegalStateException e) {
// restructure: dispatch work via context.executeBlocking instead
} Prevention
- Never nest executeBlocking calls
- Only create/unwrap worker executors from non-Vert.x threads or virtual-thread contexts
- Use context.executeBlocking when already on a Vert.x context
When it happens
Trigger: Calling WorkerExecutor API (or APIs delegating to unwrapWorkerExecutor) from inside code running on a VertxThread — i.e. inside a verticle handler, inside another executeBlocking block, or on an event-loop thread.
Common situations: Calling executeBlocking inside executeBlocking; invoking worker-pool APIs from a verticle start()/handler; migrating old blocking code that called these APIs directly from an event-loop callback; virtual-thread migration code paths.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Only the context thread can write a message
- maxEventLoopExecuteTime must be > 0
- maxWorkerpExecuteTime must be > 0
- poolSize must be > 0
- maxExecuteTime must be > 0
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/fbd5fb1be45f7162.
Report an issue: GitHub.