quarkusio/quarkus · error · IllegalStateException

No Vertx context found

Error message

No Vertx context found

What it means

FlowToPublisherHandler adapts a Kotlin Flow result into a reactive Publisher for RESTEasy Reactive. It needs the Vert.x context to construct a VertxDispatcher; if Vertx.currentContext() is null it throws IllegalStateException("No Vertx context found"). Same family as the coroutine handler context error but on the Flow-to-Publisher path.

Source

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

import jakarta.enterprise.inject.spi.CDI
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler

class FlowToPublisherHandler : ServerRestHandler {

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

    override fun handle(requestContext: ResteasyReactiveRequestContext?) {
        val result = requestContext!!.result
        if (result is Flow<*>) {
            val requestScope = requestContext.captureCDIRequestScope()
            val dispatcher: CoroutineDispatcher =
                Vertx.currentContext()?.let { VertxDispatcher(it, requestScope, requestContext) }
                    ?: throw IllegalStateException("No Vertx context found")

            val coroutineScope = CDI.current().select(ApplicationCoroutineScope::class.java)
            requestContext.suspend()
            coroutineScope.get().launch(context = dispatcher) {
                // ensure the proper CL is not lost in dev-mode
                Thread.currentThread().contextClassLoader = originalTCCL
                requestContext.result = result.asMulti()
                // run in a direct invocation executor to run the rest of the invocation in the
                // co-route scope
                // feels a bit fragile, but let's see how it goes
                requestContext.resume(Executor { it.run() })
            }
        }
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure Flow-returning endpoints are served via the standard HTTP pipeline on Vert.x event loops
  2. Avoid @Blocking on Flow-returning endpoints
  3. Call endpoints through HTTP/RestAssured in tests instead of direct method invocation
  4. Upgrade Quarkus for improved context propagation

Example fix

// before
@Blocking
@GET
fun stream(): Flow<Item> = flow { ... }
// after
@GET
fun stream(): Flow<Item> = flow { ... }
Defensive patterns

Strategy: try-catch

Validate before calling

if (io.vertx.core.Vertx.currentContext() == null) {
    throw IllegalStateException("Flow endpoint handled without Vert.x context")
}

Type guard

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

Try / catch

try {
    flowHandler.handle(requestContext)
} catch (e: IllegalStateException) {
    if (e.message?.contains("No Vertx context found") == true) {
        requestContext.handleException(e, true)
    } else throw e
}

Prevention

When it happens

Trigger: An endpoint returning Flow<...> is processed on a thread without an active Vert.x context, so VertxDispatcher cannot be created.

Common situations: Flow-returning endpoints invoked via blocking dispatch or custom thread pools; tests calling resource methods directly; threading extensions stripping the Vert.x context before result handling.

Related errors


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