quarkusio/quarkus · error · IllegalStateException

No current Mutiny.StatelessSession found - no reactive stat

Error message

No current Mutiny.StatelessSession found
	- no reactive stateless session was found in the Vert.x context and the context was not marked to open a new session lazily
	- a stateless session is opened automatically for JAX-RS resource methods annotated with an HTTP method (@GET, @POST, etc.); inherited annotations are not taken into account
	- you may need to annotate the business method with @Transactional, @WithStatelessSession or @WithStatelessTransaction

What it means

SessionOperations.getStatelessSession mirrors getSession but for Mutiny.StatelessSession: it throws IllegalStateException when the Vert.x duplicated context holds neither a reactive stateless session nor a lazily-opened stateless session marker. Stateless-session Panache operations require the context to be managed by @WithStatelessSession/@WithStatelessTransaction/@Transactional (or a stateless JAX-RS setup). It is a context-propagation failure, not a query error.

Source

Thrown at extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/SessionOperations.java:342

        Context context = vertxContext();
        // First make sure we don't already have an opened managed session
        Uni<Mutiny.StatelessSession> error = checkNoManagedSession(context, persistenceUnitName);
        if (error != null) {
            return error;
        }
        Optional<OpenedSessionsState.SessionWithKey<Mutiny.StatelessSession>> opened = OPENED_SESSIONS_STATE_STATELESS
                .getOpenedSession(context, persistenceUnitName);
        if (opened.isPresent()) {
            return Uni.createFrom().item(opened.get().session());
        } else if (ContextLocals.get(context, SESSION_ON_DEMAND_KEY, null) != null) {
            trackOnDemandSession(context, persistenceUnitName);
            return Uni.createFrom()
                    .item(() -> HibernateReactiveRecorder.getStatelessSession(persistenceUnitName, SESSION_ON_DEMAND_KEY));
        } else if (ContextLocals.get(context, TRANSACTIONAL_METHOD_KEY, null) != null) {
            return Uni.createFrom()
                    .item(() -> HibernateReactiveRecorder.getStatelessSession(persistenceUnitName, TRANSACTIONAL_METHOD_KEY));
        } else {
            throw new IllegalStateException("No current Mutiny.StatelessSession found"
                    + "\n\t- no reactive stateless session was found in the Vert.x context and the context was not marked to open a new session lazily"
                    + "\n\t- a stateless session is opened automatically for JAX-RS resource methods annotated with an HTTP method (@GET, @POST, etc.); inherited annotations are not taken into account"
                    + "\n\t- you may need to annotate the business method with @Transactional, @WithStatelessSession or @WithStatelessTransaction");
        }
    }

    /**
     * @return the current reactive session stored in the context, or {@code null} if no session exists
     */
    public static Mutiny.Session getCurrentSession(String persistenceUnitName) {
        Context context = vertxContext();
        return OPENED_SESSIONS_STATE.getOpenedSession(context, persistenceUnitName)
                .map(OpenedSessionsState.SessionWithKey::session)
                .orElse(null);
    }

    /**
     * @return the current reactive stateless session stored in the context, or {@code null} if no stateless session exists

View on GitHub (pinned to e1c734241f)

Solutions

  1. Annotate the business method with @WithStatelessSession or @WithStatelessTransaction so a stateless session is opened on demand.
  2. Use @Transactional if the operation is transactional and stateless-session backed.
  3. Ensure the HTTP-verb annotation is directly on the JAX-RS resource method (inherited annotations are ignored).
  4. Keep the work on the Vert.x duplicated context — do not hop threads with plain executors; use Mutiny emitOn with a context-preserving executor if needed.
  5. If you actually need a managed session, switch to @WithSession/stateful APIs instead of stateless ones.

Example fix

// before
public Uni<Product> refresh(Product p) {
    return Panache.withStatelessSession(s -> s.refresh(p)); // no stateless session in context
}

// after
@WithStatelessSession
public Uni<Product> refresh(Product p) {
    return Panache.withStatelessSession(s -> s.refresh(p));
}
Defensive patterns

Strategy: validation

Validate before calling

if (Vertx.currentContext() == null || !statelessMarkersPresent()) {
    throw new IllegalStateException("Stateless Panache calls require @WithStatelessSession/@WithStatelessTransaction on the calling method");
}

Try / catch

try { return Panache.withStatelessSession(s -> s.refresh(p)); } catch (IllegalStateException e) { if (e.getMessage().startsWith("No current Mutiny.StatelessSession found")) { log.error("Missing @WithStatelessSession annotation", e); } throw e; }

Prevention

When it happens

Trigger: Calling stateless-session Panache operations (e.g. via StatelessSession-backed repositories, withStatelessSession APIs) from a context without markers: unannotated business methods, background threads losing the duplicated context, schedulers, or code relying on inherited HTTP annotations on a superclass.

Common situations: Mixing @WithSession (managed session) code paths with stateless APIs — the stateless lookup finds no stateless marker; calling stateless operations from a thread pool; forgetting @WithStatelessTransaction on an event consumer.

Related errors


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