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

  1. 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
  2. If you need to run blocking work from a handler, capture the WorkerExecutor outside and schedule appropriately, or use context.executeBlocking which handles dispatch
  3. 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

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


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/fbd5fb1be45f7162. Report an issue: GitHub.