quarkusio/quarkus · error · ContextNotActiveException

Cannot use the EntityManager/Session because neither a trans

Error message

Cannot use the EntityManager/Session because neither a transaction nor a CDI request context is active. Consider adding @Transactional to your method to automatically activate a transaction, or @ActivateRequestContext if you have valid reasons not to use transactions.

What it means

TransactionScopedSession is the CDI bean backing @Inject EntityManager. When acquiring a session it first allows an active transaction; failing that, it allows a session-scoped fallback only if request-scoped sessions are enabled AND the CDI request context is active. If request-scoped sessions are enabled but no request context is active (e.g. a background thread, startup event, or scheduler), Quarkus throws ContextNotActiveException because the session could not be kept per-request consistent.

Source

Thrown at extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedSession.java:125

            }
            Session newSession = jtaSessionOpener.openSession();
            // The session has automatically joined the JTA transaction when it was constructed.
            transactionSynchronizationRegistry.putResource(sessionKey, newSession);
            // No need to flush or close the session upon transaction completion:
            // Hibernate ORM itself registers a transaction that does just that.
            // See:
            // - io.quarkus.hibernate.orm.runtime.boot.FastBootMetadataBuilder.mergeSettings
            // - org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.joinJtaTransaction
            // - org.hibernate.internal.SessionImpl.beforeTransactionCompletion
            // - org.hibernate.internal.SessionImpl.afterTransactionCompletion
            return new SessionResult(newSession, false, true);
        } else if (requestScopedSessionEnabled) {
            if (Arc.container().requestContext().isActive()) {
                RequestScopedSessionHolder requestScopedSessions = this.requestScopedSessions.get();
                return new SessionResult(requestScopedSessions.getOrCreateSession(unitName, sessionFactory),
                        false, false);
            } else {
                throw new ContextNotActiveException(
                        "Cannot use the EntityManager/Session because neither a transaction nor a CDI request context is active."
                                + " Consider adding @Transactional to your method to automatically activate a transaction,"
                                + " or @ActivateRequestContext if you have valid reasons not to use transactions.");
            }
        } else {
            throw new ContextNotActiveException(
                    "Cannot use the EntityManager/Session because no transaction is active."
                            + " Consider adding @Transactional to your method to automatically activate a transaction,"
                            + " or set '" + HibernateOrmRuntimeConfig.extensionPropertyKey("request-scoped.enabled")
                            + "' to 'true' if you have valid reasons not to use transactions.");
        }
    }

    private void checkBlocking() {
        if (!BlockingOperationControl.isBlockingAllowed()) {
            throw new BlockingOperationNotAllowedException(
                    "You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread.");
        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Annotate the method (or class) with @Transactional so acquireSession takes the transaction path instead of the request-context path.
  2. Wrap the method in @ActivateRequestContext if you deliberately want a request-scoped session without a transaction (finds/reads only; writes still need a transaction).
  3. Move the ORM work into a request-scoped or transactional CDI bean rather than running it directly on a background/event-loop thread.
  4. If sessions should only be transaction-scoped, set quarkus.hibernate-orm.request-scoped.enabled=false so the error points to the missing transaction instead.

Example fix

// before
@Scheduled(every = "10s")
void cleanup() {
    em.remove(stale); // ContextNotActiveException
}

// after
@Scheduled(every = "10s")
@Transactional
void cleanup() {
    em.remove(stale);
}
Defensive patterns

Strategy: validation

Validate before calling

import io.quarkus.arc.Arc;

boolean safeToUseEm() {
    return Arc.container().requestContext().isActive()
        || Arc.container().beanManager()
              .resolveBeanManager() != null && isTransactionActive();
}
// Simplest pre-check before any EM call on background threads:
boolean requestActive() {
    return Arc.container() != null && Arc.container().requestContext().isActive();
}

Try / catch

try {
    em.persist(entity);
} catch (ContextNotActiveException e) {
    // request context not active: fall back to a transactional bean call
    transactionalService.persist(entity);
}

Prevention

When it happens

Trigger: Calling any TransactionScopedSession method (persist, merge, remove, find, findMultiple, getReference) via an injected EntityManager when: quarkus.hibernate-orm.request-scoped.enabled=true, there is no active @Transactional, and Arc.container().requestContext().isActive() is false — typically code running on a non-HTTP thread (Vert.x event loop/callback, @Scheduled executor, manually spawned thread, or outside a request scope).

Common situations: Running Hibernate calls from a @Scheduled task or Kafka/Vert.x message consumer without @Transactional; doing ORM work in an ApplicationStarted observer or on a plain new Thread; unit-style code calling the injected EM during startup; tests that invoke EM methods outside a request scope.

Related errors


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