{"record":{"id":"ece8f95a806d4cb2","repo":"quarkusio/quarkus","slug":"no-vertx-context-found","errorCode":null,"errorMessage":"No Vertx context found","messagePattern":"No Vertx context found","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt","lineNumber":34,"sourceCode":"    private val invoker: EndpointInvoker,\n    private val coroutineScope: CoroutineScope,\n) : ServerRestHandler {\n\n    private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader\n\n    override fun handle(requestContext: ResteasyReactiveRequestContext) {\n        if (requestContext.result != null) {\n            return\n        }\n        if (invoker !is CoroutineEndpointInvoker) {\n            requestContext.handleException(IllegalStateException(\"Not a coroutine invoker\"), true)\n            return\n        }\n\n        val requestScope = requestContext.captureCDIRequestScope()\n        val dispatcher: CoroutineDispatcher =\n            Vertx.currentContext()?.let { VertxDispatcher(it, requestScope, requestContext) }\n                ?: throw IllegalStateException(\"No Vertx context found\")\n\n        logger.trace(\"Handling request with dispatcher {}\", dispatcher)\n\n        requestContext.suspend()\n        val done = AtomicBoolean()\n        val canceled = AtomicBoolean()\n\n        val job =\n            coroutineScope.launch(context = dispatcher) {\n                // ensure the proper CL is not lost in dev-mode\n                Thread.currentThread().contextClassLoader = originalTCCL\n                try {\n                    val result =\n                        invoker.invokeCoroutine(\n                            requestContext.endpointInstance,\n                            requestContext.parameters,\n                        )\n                    done.set(true)","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt#L16-L52","documentation":"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\").","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure the request goes through the standard RESTEasy Reactive HTTP pipeline so Vert.x dispatches it on an event loop","Do not call suspend endpoint methods directly from worker/blocking threads or plain unit tests","If using custom threading, keep the Vert.x context attached (use Vertx.getOrCreateContext or run within context.runOnContext)","Upgrade Quarkus — newer versions handle context propagation more robustly"],"exampleFix":"// before (direct call in test)\nval result = myResource.get(\"id\")\n// after\n@Test\nfun test() {\n    given().get(\"/my-resource\").then().statusCode(200)\n}","handlingStrategy":"try-catch","validationCode":"// Guard before invoking coroutine handling\nif (io.vertx.core.Vertx.currentContext() == null) {\n    throw IllegalStateException(\"suspend endpoint invoked without a Vert.x context\")\n}","typeGuard":"fun hasVertxContext(): Boolean = io.vertx.core.Vertx.currentContext() != null","tryCatchPattern":"try {\n    handler.handle(requestContext)\n} catch (e: IllegalStateException) {\n    if (e.message?.contains(\"No Vertx context found\") == true) {\n        // fall back to non-coroutine dispatch or fail the request with 500\n    }\n    throw e\n}","preventionTips":["Always serve suspend endpoints via the HTTP pipeline, not direct calls","Avoid @Blocking on suspend endpoints","Use RestAssured/HTTP client in tests rather than invoking resource methods directly"],"tags":["kotlin","coroutines","vertx","threading","resteasy-reactive"],"backgroundTag":"missing-vertx-context","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}