quarkusio/quarkus · error · IllegalStateException

No Vertx context found

Error message

No Vertx context found

What it means

AbstractCoroutineInvoker launches a Kotlin coroutine to run the @Scheduled suspend function on the ApplicationCoroutineScope, using a Vertx dispatcher. It requires the invocation to run on a Vert.x duplicated/context thread; if Vertx.currentContext() returns null the coroutine cannot be dispatched and this IllegalStateException is thrown.

Source

Thrown at extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/AbstractCoroutineInvoker.kt:18

package io.quarkus.scheduler.kotlin.runtime

import io.quarkus.arc.Arc
import io.quarkus.scheduler.ScheduledExecution
import io.quarkus.scheduler.common.runtime.ScheduledInvoker
import io.vertx.core.Vertx
import java.util.concurrent.CompletionStage
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture

abstract class AbstractCoroutineInvoker : ScheduledInvoker {

    override fun invoke(execution: ScheduledExecution): CompletionStage<Void> {
        val coroutineScope = Arc.container().instance(ApplicationCoroutineScope::class.java).get()
        val dispatcher: CoroutineDispatcher =
            Vertx.currentContext()?.let(::VertxDispatcher)
                ?: throw IllegalStateException("No Vertx context found")

        return coroutineScope
            .async(context = dispatcher) { invokeBean(execution) }
            .asCompletableFuture()
    }

    abstract suspend fun invokeBean(execution: ScheduledExecution): Void
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Let Quarkus invoke the scheduled method through its normal pipeline rather than calling the invoker manually
  2. Ensure the application depends on quarkus-vertx so Vert.x context is available
  3. In tests, use @QuarkusTest so the runtime establishes the Vert.x context
  4. If running custom threading, wrap the call in vertx.runOnContext or executeBlocking

Example fix

// before (manual invocation in plain test)
invoker.invoke(execution)
// after
@QuarkusTest
class JobTest { /* trigger the schedule via Quarkus runtime */ }
Defensive patterns

Strategy: try-catch

Validate before calling

if (Vertx.currentContext() == null) {
    throw new IllegalStateException("invoke() must run on a Vert.x context");
}

Try / catch

try { invoker.invoke(execution).toCompletableFuture().get(); }
catch (ExecutionException e)
    when (e.getCause() instanceof IllegalStateException &&
          e.getCause().getMessage().contains("No Vertx context")) { /* run via Quarkus runtime */ }

Prevention

When it happens

Trigger: Invoking a suspend @Scheduled method from a thread that is not attached to a Vert.x context (e.g. custom invoker wiring, manual invocation of the ScheduledInvoker, or running outside the scheduled execution pipeline).

Common situations: Calling the invoker directly in tests without Quarkus runtime; embedding scheduler code in a non-Vert.x thread; misconfigured execution model where scheduled invocations bypass Vert.x.

Related errors


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