quarkusio/quarkus · error · BlockingOperationNotAllowedException
Blocking security check attempted in code running on the eve
Error message
Blocking security check attempted in code running on the event loop. Make the secured method return an async type, i.e. Uni, Multi or CompletionStage, or use an authentication mechanism that sets the SecurityIdentity in a blocking manner prior to delegating the call
What it means
A synchronous (@RolesAllowed/@Authenticated) security check attempted to resolve the current SecurityIdentity by blocking while the request ran on the IO event loop thread, which Quarkus forbids (BlockingOperationNotAllowedException rethrown with guidance). Reactive/RESTEasy-reactive pipelines cannot block the event loop, so authentication that requires blocking must be done before dispatch or the endpoint must be async.
Source
Thrown at extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java:68
boolean securityEventsEnabled = ConfigProvider.getConfig().getValue("quarkus.security.events.enabled",
Boolean.class);
this.securityEventHelper = new SecurityEventHelper<>(authZSuccessEvent, authZFailureEvent, AUTHORIZATION_SUCCESS,
AUTHORIZATION_FAILURE, beanManager, securityEventsEnabled);
} else {
// static interceptors are initialized during the static init, therefore we need to initialize the helper lazily
this.securityEventHelper = SecurityEventHelper.lazilyOf(authZSuccessEvent, authZFailureEvent,
AUTHORIZATION_SUCCESS, AUTHORIZATION_FAILURE, beanManager);
}
}
public void check(Method method, Object[] parameters) {
SecurityCheck securityCheck = storage.getSecurityCheck(method);
SecurityIdentity identity = null;
if (securityCheck != null && !securityCheck.isPermitAll()) {
try {
identity = securityIdentityAssociation.get().getIdentity();
} catch (BlockingOperationNotAllowedException blockingException) {
throw new BlockingOperationNotAllowedException(
"Blocking security check attempted in code running on the event loop. " +
"Make the secured method return an async type, i.e. Uni, Multi or CompletionStage, or " +
"use an authentication mechanism that sets the SecurityIdentity in a blocking manner " +
"prior to delegating the call",
blockingException);
}
if (securityEventHelper.fireEventOnFailure()) {
try {
securityCheck.apply(identity, method, parameters);
} catch (Exception exception) {
fireAuthZFailureEvent(identity, exception, securityCheck, method);
throw exception;
}
} else {
securityCheck.apply(identity, method, parameters);
}
}
if (securityEventHelper.fireEventOnSuccess()) {View on GitHub (pinned to e1c734241f)
Solutions
- Make the secured method return an async type (Uni, Multi, or CompletionStage) so the check runs on a worker thread
- Annotate the class/method with @RunOnVirtualThread or @Blocking so the security check executes on a blocking thread
- Configure the authentication mechanism to resolve the SecurityIdentity proactively/blocking before the call (e.g. enable proactive OIDC auth)
- Move blocking identity resolution into a non-blocking IdentityProvider
Example fix
// before
@RolesAllowed("admin")
public String secret() { ... } // event-loop call, blocking identity lookup
// after
@RolesAllowed("admin")
public Uni<String> secret() { return Uni.createFrom().item(...); } Defensive patterns
Strategy: try-catch
Validate before calling
// prefer checking the execution model before relying on blocking identity access
boolean onEventLoop = Vertx.currentContext() != null;
if (onEventLoop && requiresBlockingIdentity()) {
// switch to async return type or @Blocking / @RunOnVirtualThread
} Try / catch
try {
identity = securityIdentityAssociation.get().getIdentity();
} catch (BlockingOperationNotAllowedException e) {
throw new BlockingOperationNotAllowedException(
"Use Uni/Multi/CompletionStage return type or @Blocking/@RunOnVirtualThread", e);
} Prevention
- Return Uni/Multi/CompletionStage from secured reactive endpoints
- Use @Blocking or @RunOnVirtualThread when the identity lookup must block
- Enable proactive authentication so the identity is resolved before the interceptor
- Test secured reactive endpoints with quarkus-resteasy-reactive to catch this early
When it happens
Trigger: Securing a reactive endpoint (returns Uni/Multi/CompletionStage, or plain object on a reactive execution model) whose identity is not yet resolved at interceptor time and whose auth mechanism would block; calling identity.get() on the event loop in a RESTEasy Reactive app.
Common situations: Mixed blocking/reactive setups: JPA-based identity provider called from a reactive endpoint; quarkus-oidc without proactive auth combined with synchronous @RolesAllowed on a reactive resource; after migration from RESTEasy Classic to RESTEasy Reactive.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Cannot call getIdentity() from the IO thread when lazy authe
- Blocking gRPC client call made from the event loop. If the c
- You have attempted to perform a blocking operation on a IO t
- You have attempted to perform a blocking operation on a IO t
- @Transactional cannot start a JTA transaction within a react
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/7f49b13007b9f4e1.
Report an issue: GitHub.