quarkusio/quarkus · error · IllegalStateException

No Vertx context found

Error message

No Vertx context found

What it means

CoroutineInvocationHandler.handle requires a Vert.x duplicated/context to build a VertxDispatcher for resuming the coroutine on the right event-loop context. When Vertx.currentContext() returns null (no Vert.x context is active on the current thread), it throws IllegalStateException("No Vertx context found").

Source

Thrown at extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt:34

    private val invoker: EndpointInvoker,
    private val coroutineScope: CoroutineScope,
) : ServerRestHandler {

    private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

    override fun handle(requestContext: ResteasyReactiveRequestContext) {
        if (requestContext.result != null) {
            return
        }
        if (invoker !is CoroutineEndpointInvoker) {
            requestContext.handleException(IllegalStateException("Not a coroutine invoker"), true)
            return
        }

        val requestScope = requestContext.captureCDIRequestScope()
        val dispatcher: CoroutineDispatcher =
            Vertx.currentContext()?.let { VertxDispatcher(it, requestScope, requestContext) }
                ?: throw IllegalStateException("No Vertx context found")

        logger.trace("Handling request with dispatcher {}", dispatcher)

        requestContext.suspend()
        val done = AtomicBoolean()
        val canceled = AtomicBoolean()

        val job =
            coroutineScope.launch(context = dispatcher) {
                // ensure the proper CL is not lost in dev-mode
                Thread.currentThread().contextClassLoader = originalTCCL
                try {
                    val result =
                        invoker.invokeCoroutine(
                            requestContext.endpointInstance,
                            requestContext.parameters,
                        )
                    done.set(true)

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure the request goes through the standard RESTEasy Reactive HTTP pipeline so Vert.x dispatches it on an event loop
  2. Do not call suspend endpoint methods directly from worker/blocking threads or plain unit tests
  3. If using custom threading, keep the Vert.x context attached (use Vertx.getOrCreateContext or run within context.runOnContext)
  4. Upgrade Quarkus — newer versions handle context propagation more robustly

Example fix

// before (direct call in test)
val result = myResource.get("id")
// after
@Test
fun test() {
    given().get("/my-resource").then().statusCode(200)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Guard before invoking coroutine handling
if (io.vertx.core.Vertx.currentContext() == null) {
    throw IllegalStateException("suspend endpoint invoked without a Vert.x context")
}

Type guard

fun hasVertxContext(): Boolean = io.vertx.core.Vertx.currentContext() != null

Try / catch

try {
    handler.handle(requestContext)
} catch (e: IllegalStateException) {
    if (e.message?.contains("No Vertx context found") == true) {
        // fall back to non-coroutine dispatch or fail the request with 500
    }
    throw e
}

Prevention

When it happens

Trigger: Invoking a Kotlin suspend endpoint outside the normal Vert.x event-loop dispatch path — e.g. from a thread that was never attached to a Vert.x context (custom thread pool, direct bean method call, blocking pipeline stage).

Common situations: Calling resource methods directly in tests without the HTTP layer; running the endpoint through custom executors; misconfigured threading extensions that move the request off Vert.x contexts before the coroutine handler runs.

Related errors


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