quarkusio/quarkus · error · IllegalStateException

No current Vertx context found

Error message

No current Vertx context found

What it means

The interceptor needs the current Vert.x duplicated Context to store transaction/session state safely. If Vertx.currentContext() returns null, the code is not running on a Vert.x (event-loop/virtual-thread) context, so it throws IllegalStateException("No current Vertx context found").

Source

Thrown at extensions/reactive-transactions/runtime/src/main/java/io/quarkus/reactive/transaction/runtime/TransactionalInterceptorBase.java:305

                    "Calling a method annotated with @Transactional from a method annotated with @ReactiveTransactional is not supported. "
                            + "Use either @Transactional or @WithSessionOnDemand/@WithSession/@WithTransaction, "
                            + "but not both, throughout your whole application.");
        }
    }

    /**
     *
     * @return the current vertx duplicated context
     * @throws IllegalStateException If no vertx context is found or is not a safe context as mandated by the
     *         {@link VertxContextSafetyToggle}
     */
    private static Context vertxContext() {
        Context context = Vertx.currentContext();
        if (context != null) {
            VertxContextSafetyToggle.validateContextIfExists(ERROR_MSG, ERROR_MSG);
            return context;
        } else {
            throw new IllegalStateException("No current Vertx context found");
        }
    }

    // Default impl fails .REQUIRED overrides it
    protected void validateTransactionalType(InvocationContext context) {
        Transactional transactional = context.getMethod().getAnnotation(Transactional.class);
        if (transactional != null && transactional.value() != Transactional.TxType.REQUIRED) {
            throw new UnsupportedOperationException(
                    "@Transactional on Reactive methods supports only Transactional.TxType.REQUIRED");
        }
    }

    /**
     * <p>
     * Looking for the {@link Transactional} annotation first on the method,
     * second on the class.
     * <p>
     * Method handles CDI types to cover cases where extensions are used. In

View on GitHub (pinned to e1c734241f)

Solutions

  1. Run the call on a Vertx-managed thread, e.g. wrap in uni.emitOn(quarkus executor) or invoke from an event-loop/RESTEasy Reactive endpoint.
  2. Use Quarkus's managed executors (@Inject ManagedExecutor / Vertx.getOrCreate(context)) so the duplicated context is propagated.
  3. For scheduled/background work, use QuarkusScheduler (@Scheduled with reactive support) which preserves Vertx contexts.

Example fix

// before
CompletableFuture.runAsync(() -> service.save(item)); // no Vertx context
// after
Uni.createFrom().item(item)
   .emitOn(Infrastructure.getDefaultWorkerPool())
   .chain(i -> service.save(i)) // interceptor runs with propagated duplicated context
Defensive patterns

Strategy: validation

Validate before calling

// guard before invoking a reactive transactional method off the Vertx event loop
if (Vertx.currentContext() == null)
    throw new IllegalStateException("Not on a Vertx context; run via event loop or emitOn/ManagedExecutor");

Try / catch

try {
    return service.save(item);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("No current Vertx context")) {
        log.warn("Called off Vertx context; rerun on Vertx-managed thread");
    }
    throw e;
}

Prevention

When it happens

Trigger: Invoking a @Transactional/@ReactiveTransactional reactive method from a plain thread (e.g. CompletableFuture.runAsync, custom executor, scheduled task, or main thread) that has no Vertx duplicated context.

Common situations: Calling reactive transactional beans from @Scheduled tasks, tests without QuarkusVertxTest support, or manually spawned threads; blocking the chain and resuming on a non-Vertx thread.

Related errors


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